(function(w,d,s,l,i){w[l]=w[l]|||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=? 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-5LHNRP9'); GraphQL/MongoDB API izvēršana, izmantojot Netlify funkcijas - The Codest
The Codest
  • Par mums
  • Pakalpojumi
    • Programmatūras izstrāde
      • Frontend izveide
      • Backend izstrāde
    • Staff Augmentation
      • Frontend izstrādātāji
      • Backend izstrādātāji
      • Datu inženieri
      • Mākoņa inženieri
      • QA inženieri
      • Citi
    • Tā Konsultatīvais dienests
      • Audits un konsultācijas
  • Nozares
    • Fintech un banku darbība
    • E-commerce
    • Adtech
    • Healthtech
    • Ražošana
    • Loģistika
    • Automobiļu nozare
    • IOT
  • Vērtība par
    • CEO
    • CTO
    • Piegādes vadītājs
  • Mūsu komanda
  • Case Studies
  • Zināt, kā
    • Blogs
    • Tikšanās
    • Tiešsaistes semināri
    • Resursi
Karjera Sazinieties ar mums
  • Par mums
  • Pakalpojumi
    • Programmatūras izstrāde
      • Frontend izveide
      • Backend izstrāde
    • Staff Augmentation
      • Frontend izstrādātāji
      • Backend izstrādātāji
      • Datu inženieri
      • Mākoņa inženieri
      • QA inženieri
      • Citi
    • Tā Konsultatīvais dienests
      • Audits un konsultācijas
  • Vērtība par
    • CEO
    • CTO
    • Piegādes vadītājs
  • Mūsu komanda
  • Case Studies
  • Zināt, kā
    • Blogs
    • Tikšanās
    • Tiešsaistes semināri
    • Resursi
Karjera Sazinieties ar mums
Atpakaļ bultiņa ATGRIEZTIES ATPAKAĻ
2020-10-02
Programmatūras izstrāde

GraphQL/MongoDB API izvēršana, izmantojot Netlify funkcijas

The Codest

Pawel Rybczynski

Software Engineer

Mērķi Sākotnējā uzstādīšana Instalēt atkarības Sāksim Vispirms pievienojiet tsconfig.json galvenajā direktorijā: Tagad izveidosim src/server.ts serveru implementācijām. Tad pievienojiet divas funkcijas: vienu vietējam serverim un otru lambda. Labi, mums nav neviena resolvera vai tipa definīcijas, tāpēc mums tās ir jāizveido. Pieņemsim, ka sākumā mēs vēlamies, lai [...]

Mērķi

  1. Konfigurējiet gan vietējos, gan lambda serverus.
  2. Savienojiet abus ar MongoDB.
  3. Ieviest pamata autentifikāciju.
  4. Serverless Apollo izvietošana GraphQL API ar Netlify.
  5. Rakstveidlapas izmantošana.

Sākotnējā iestatīšana

npm init -y

Instalēt atkarības

npm install --save mašīnraksts graphql aws-lambda @types/aws-lambda

Sāksim

Vispirms pievienojiet tsconfig.json uz galveno direktoriju:

 {
 "compilerOptions": {
 "target": "es5",
 "module": "commonjs",
 "allowJs": true,
 "strict": true,
 "esModuleInterop": true,
 "skipLibCheck": true,
 "forceConsistentCasingInFileNames": true
 },
 "include": ["src/*.ts", "src/**/*.ts", "src/**/*.js"],
 "exclude": ["node_modules"]].
 }

Tagad izveidosim src/server.ts serveru implementācijām. Tad pievienojiet divas funkcijas: vienu vietējam serverim un otru lambda.

// src/server.ts
importēt { ApolloServer as ApolloServerLambda } no "apollo-server-lambda";
import { ApolloServer } no "apollo-server";


const createLambdaServer = () =>
  new ApolloServerLambda({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    },
  });

const createLocalServer = () =>
  jauns ApolloServer({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    },
  });

eksportēt { createLambdaServer, createLocalServer };

Labi, mums nav neviena resolvera vai tipa definīcijas, tāpēc mums tās ir jārada. Pieņemsim, ka sākumā mēs vēlamies izveidot lietotājus un saņemt par tiem informāciju.

// src/schemas.ts
const { gql } = require("apollo-server-lambda");

const userSchema = gql`
  tips User {
    id: ID!
    email: : e-pasta adrese: email: email: string!
    name: String!
  }

  tips Query {
    user(id: ID!): Lietotājs!
  }

  tips Mutācija {
    createUser(name: String!, email: String!, password: String!): Lietotājs!
  }
`;

Ja neesat par to informēts, Apollo sagatavoja ļoti jauku pamācību

Tagad izveidosim lietotāja resolveri ar vienu vaicājumu un vienu mutāciju.

// src/resolvers.ts
const userResolver = {
  Query: {
    user: async (parent, args, context, info) => {
      {...}
    },
  },
  Mutācija: {
    createUser: async (parent, args, context, info) => {
      {...}
    },
  },
};

Bet mums nav nekādu datu... Labosim to 😉

Neaizmirstiet importēt tipa definīciju un resolveri uz serveri.

// src/server.ts
importēt { ApolloServer as ApolloServerLambda } no "apollo-server-lambda";
import { ApolloServer } no "apollo-server";

importēt { typeDefs } no "./schemas";
importēt { resolvers } no "./resolvers";

{...}

Savienojums ar MongoDB, izmantojot mongoose

Tagad ir īstais laiks izveidot savienojumu ar mūsu datubāzi. Šajā konkrētajā gadījumā tā būs MongoDB. Tā ir bezmaksas un viegli kopjama. Bet pirms tam instalēsim vēl divas atkarības:

npm install --save mongoose dotenv

Pirmais solis ir izveidot lietotāja modeli.

// src/model.ts
importēt mongoose, { Document, Error, Schema } no "mongoose";

eksportēt tipu User = Document & {
  _id: string,
  email: string,
  name: string,
  parole: string,
};

dzēst mongoose.connection.models["User"];

const UserSchema: Schema = new Schema({
  email: {
    tips: ,
    required: true,
    unikāls: true,
  },
  name: {
    tips: :: string,
    obligāts: true,
    minLength: 3,
    maxLength: 32,
  },
  parole: {
    tips: :: string,
    obligāts: true,
  },
});

export const userModel = mongoose.model  ("Lietotājs", UserSchema);

Paroles padariet drošākas

Drošība pirmajā vietā! Tagad nodrošināsim paroles, tās šifrējot.

npm install --save bcrypt @types/bcrypt

Tagad ievietojiet paroles drošību pirms starpprogrammatūras izsaukuma atgriezeniskajā saitē. Pre-middleware funkcijas tiek izpildītas viena pēc otras, kad katra starpprogrammatūra izsauc nākamo. Lai paroles būtu drošas, mēs izmantojam metodi, kas ģenerē sāli un hash atsevišķiem funkciju izsaukumiem.

// src/model.ts
importēt bcrypt no "bcrypt";
{...}
const SALT_WORK_FACTOR: number = 10;

UserSchema.pre("save", function (next) {
  const user = this as User;
  if (!this.isModified("password")) return next();

  bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
    if (err) return next(err);

    bcrypt.hash(user.password, salt, function (err, hash) {
      if (err) return next(err);
      user.password = hash;
      next();
    });
  });
});
{...}

Pēc tam pievienojiet metodi comparePasswords uz UserSchema:

// src/model.ts
{...}
UserSchema.methods.comparePasswords = funkcija (
  candidatePassword: string,
  cb: (err: Error | null, same: boolean | null) => void
) {
  const user = this as User;
  bcrypt.compare(candidatePassword, user.password, (err, isMatch) => {
    if (err) {
      return cb(err, null);
    }
    cb(null, isMatch);
  });
};
{...}

Un, protams, modificējiet Lietotāja tips.

tips comparePasswordFunction = (
  candidatePassword: string,
  cb: (err: Error, isMatch: boolean) => void
) => void;

eksportēt tipu User = Document & {
  _id: string,
  email: string,
  name: string,
  parole: string,
  comparePasswords: comparePasswordFunction,
};

Tagad varam izveidot savienojumu starp serveriem un datubāzēm. MONGODB_URI ir vides mainīgais, kas satur savienojuma virkni, kura nepieciešama savienojuma izveidei. To var iegūt klastera panelī pēc tam, kad esat pieteicies savā MongoDB atlas kontā. Ievietojiet to .env

// .env
MONGODB_URI = ....;

Vienmēr atcerieties pievienot šo failu .gitignore. Lieliski! Tagad pievienosim funkciju, kas ļauj izveidot savienojumu ar db.

// src/server.ts
importēt mongoose, { Connection } no "mongoose";
{...}
let cachedDb: Connection;

const connectToDatabase = async () => {
  if (cachedDb) return;

  await mongoose.connect(process.env.MONGODB_URI || "", {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useFindAndModify: false,
    useCreateIndex: true,
  });
  cachedDb = mongoose.connection;
};
{...}

Konteksts ir objekts, kas ir koplietojams visiem resolveriem. Lai to nodrošinātu, mums tikai jāpievieno konteksta inicializācijas funkcija ApolloServer konstruktoram. Izdarīsim to.

// src/server.ts
importēt { userModel } no "./models/user.model";
{...}
const createLambdaServer = async () =>
  new ApolloServerLambda({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    context: async () => {
      await connectToDatabase();

      return {
        models: {
          userModel,
        },
      };
    },
  });

const createLocalServer = () =>
  jauns ApolloServer({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    context: async () => {
      await connectToDatabase();

      return {
        models: {
          userModel,
        },
      };
    }
  });

Kā redzat, mēs nododam arī userModel izmantojot konteksts. Tagad mēs varam atjaunināt resolveri:

// resolvers.ts
const userResolver = {
  Query: {
    user: async (_, { email, name, password }, { models: { userModel } }) => { {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutācija: {
    createUser: async (_, { id }, { models: { userModel } }) => { {
      const user = await userModel.create({ email, name, password });
      return user;
    },
  },
};

Izskatās jauki! Tagad izveidojiet pamata servera gadījumu:

// src/index.ts
importēt { createLocalServer } no "./server";
require("dotenv").config();

const port = process.env.PORT || 4000;

const server = createLocalServer();

server.listen(port).then(({ url }) => {
  console.log(`Serveris ir darbojas pie ${url}`);
});

Vietējā servera palaišana ar Nodemon

Pēdējā lieta pirms palaist, pievienot nodemon.json

{
  "watch": ["src"],
  "ext": ".ts,.js",
  "ignore": [],
  "exec": "ts-node --transpile-only ./src/index.ts".
}

Pievienot skriptu package.json

"skripti": {
    "start": "nodemon"
  },

Un skrieniet!

npm sākt

Šāda informācija jums būtu jāsaņem termināļa iekšpusē:

Serveris ir darbojas http://localhost:4000/

Ja jā, atveriet http://localhost:4000/ pārlūkprogrammā.

Jāparādās GraphQL spēļu laukumam. Izveidosim jaunu lietotāju!

createUser.png

Aplūkojiet, kā tas izskatās datu bāzē.

datubāzeJohnDoe.png

Forši! Viss darbojas labi!

Mēģināsim iegūt informāciju par lietotāju.

userWithoutAuth.png

Un pievienojiet paroles lauku...

userPassword.png

Jauki! Mēs saņemam kļūdu Nevar veikt vaicājumu laukā "parole" par tipu "Lietotājs"."". Kā varat pārbaudīt, mēs nepievienojām šo lauku lietotāja tipa definīcijā. Tas tur ir ievietots ar nolūku. Mums nevajadzētu radīt iespēju pieprasīt jebkuru paroli vai citu sensitīvu informāciju. dati.

Vēl viena lieta... Mēs varam iegūt lietotāja datus bez autentifikācijas... tas nav labs risinājums. Mums tas ir jālabo.

Bet pirms...

Konfigurēt Codegen

Izmantosim GraphQL kods ģenerators, lai iegūtu saderīgu bāzes tipu, pamatojoties uz mūsu shēmu.

npm install --save @graphql-codegen/cli @graphql-codegen/introspection
@graphql-codegen/typescript @graphql-codegen/typescript-resolvers

Izveidot codegen.yml

pārrakstīt: true
shēma: "http://localhost:4000"
ģenerē:
  ./src/generated/graphql.ts:
    plugins:
      - "typescript"
      - "typescript-resolvers"
  ./graphql.schema.json:
    plugins:
      - "introspection"

Pievienot codegen skriptu uz package.json

"skripti": {
    "start": "nodemon",
    "codegen": "graphql-codegen --config ./codegen.yml",
  },

Pēc tam, kad darbojas vietējais serveris, palaidiet skriptu:

npm palaist codegen

Veiksmes gadījumā saņemsiet ziņojumu:

  √ Izanalizēt konfigurāciju
  √ Izvades ģenerēšana

Ja saņemat šo informāciju, jāparādās diviem failiem:

  • graphql.schema.json galvenajā direktorijā
  • graphql.ts jaunizveidotajā ceļā src/generated

Mūs vairāk interesē otrais variants. Ja to atvērsiet, pamanīsiet jauku tipu struktūru.

Tagad mēs varam uzlabot savus atrisinātājus:

// src/resolvers.ts
importēt { Resolvers, Token, User } no "./generated/graphql";

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): Promise => {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutācija: {
    createUser: async (
      _,
      { e-pasts, vārds, parole },
      { models: { userModel } }
    ): solījums => {
      const user = await userModel.create({
        email,
        name,
        parole,
      });
      return user;
    },
  },
};

Autentifikācija

Tālāk iestatīsim vienkāršu autentifikāciju, kas balstīta uz žetonu.

npm install --save jsonwebtoken @types/jsonwebtoken

Izveidot checkAuth funkcija, lai pārbaudītu, vai žetons ir derīgs. Rezultātu pievienosim kontekstam, lai tam varētu piekļūt resolveros.

// src/server.ts
import { IncomingHttpHeaders } from "http";
import {
  APIGatewayProxyEvent,
  APIGatewayProxyEventHeaders,
  Context,
} from "aws-lambda";
import jwt from "jsonwebtoken";
{...}
const checkAuth = async ({ token } : APIGatewayProxyEventHeaders | IncomingHttpHeaders ) => {
  if (typeof token === "string") {
    try {
      return await jwt.verify(token, "riddlemethis");
    } catch (e) {
      throw new AuthenticationError(`Your session expired. Sign in again.`);
    }
  }
};
{...}
const createLambdaServer = async (
  { headers }: APIGatewayProxyEvent,
  context: Context
) => {
  return new ApolloServerLambda({
    typeDefs,
    resolvers,
    context: async () => {
      await connectToDatabase();

      const auth = await checkAuth(headers);

      return {
        auth,
        models: {
          userModel,
        },
      };
    },
  });
};

function createLocalServer() {
  return new ApolloServer({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    context: async ({ req: { headers } = {} }) => {
      const auth = await checkAuth(headers);
      await connectToDatabase();

      return {
         auth,
         models: {
          userModel,
        },
      };
    },
  });
}

Mums ir nepieciešams arī veids, kā izveidot šādu žetonu. Vislabākais veids ir implementēt pieteikšanās vaicājumu mūsu resolverī.

// resolvers.ts
importēt { Resolvers, Token, User } no "./generated/graphql";

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): Promise => {
      if (!auth) throw new AuthenticationError("Jūs neesat autentificēts");

      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
    login: async (
      _,
      { e-pasts, parole },
      { models: { userModel } }
    ): Solījums => {
      const user = await userModel.findOne({ email }).exec();

      if (!user) throw new AuthenticationError("Invalid credentials");

      const matchPasswords = bcrypt.compareSync(password, user.password);

      if (!matchPasswords) throw new AuthenticationError("Invalid credentials");

      const token = jwt.sign({ id: user.id }, "riddlemethis", {
        expiresIn: 60,
      });

      return { token };
    },
  },
  Mutācija: {
    createUser: async (
      _,
      { e-pasts, vārds, parole },
      { models: { userModel } }
    ): solījums => {
      const user = await userModel.create({
        email,
        name,
        parole,
      });
      return user;
    },
  },
};

Jums ir jāatjaunina arī lietotāja tipa definīcijas pēc žetona tipa un pieteikšanās vaicājuma.

tips Token {
    token: string!
  }

tips Query {
    user(id: ID!): Lietotājs!
    login(email: String!, password: String!): Token!
  }

Mēģināsim iegūt lietotāju bez žetona

noAuth.png

Labi, darbojas labi! Mēģiniet pieteikties

token.png

Mēģināsim vēlreiz iegūt lietotāju, bet šoreiz ar galvenēs pievienotu žetonu.

withToken.png

Forši! Un ko darīt, ja mēs iestatām nepareizus akreditācijas datus?

wrongEmail.png

Jauki!

Sagatavošanās izvietošanai

Pēdējais solis: izvietojiet serverless api ar Netlify!

Izveidot mapi lambda galvenajā dir un ievietot tajā divus failus:

Pirmais satur AWS apstrādātājs. Tā izveido ApolloServerLambda servera gadījumu un
pēc tam atklājiet apstrādātāju, izmantojot šī gadījuma createHandler.

// lambda/graphql.ts
importēt { APIGatewayProxyEvent, Context } no "aws-lambda";
import { createLambdaServer } no "????";

export const handler = async (
  event: APIGatewayProxyEvent,
  context: Context
) => {
  const server = await createLambdaServer(event, context);

  return new Promise((res, rej) => {
    const cb = (err: Error, args: any) => (err ? rej(err) : res(args));
    server.createHandler()(event, context, cb);
  });
};

Varat par to izlasīt vairāk.

Otrais ir tsconfig. Svarīga daļa ir outDir lauka.

// lambda/tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es6",
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "mezgls",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "outDir": /dist": "./dist"
  },
  "include": ["./*.ts", "./**/*.ts", "./**/*.js"]]
}

Taču ir problēma. Mēs nevaram izmantot /src dir, jo Netlify nevar sasniegt ārpus lambda mapes. Tāpēc mums tas ir jāsavieno.

npm install --save ncp

Tā ir pakete, kas ļauj kopēt direktoriju.

Pievienot komplekts skriptu uz package.json

"skripti": {
    "bundle": "ncp ./src ./lambda/bundle",
    "codegen": "graphql-codegen --config ./codegen.yml",
    "start": "nodemon",
  },

Tagad, ja palaižat npm palaist paketi, jūs san redzēt, ka jaunizveidotajā lambda/bundle dir mums ir visi faili no src/.

Atjaunināt importa ceļu iekšpusē lambda/graphql.ts

// lambda/graphql.ts
importēt { APIGatewayProxyEvent, Context } no "aws-lambda";
importēt { createLambdaServer } no "./bundle/server";

{...}

Tagad jūs varat pievienot lambda/bundle dir uz .gitignore

Izvietošana ar Netlify

Mums ir jānorāda Netlify, kāda ir izveides komanda un kur atrodas mūsu funkcijas. Lai to izdarītu, izveidosim netlify.toml file:

// netlify.toml
[būvēt]
  komanda = "npm run build:lambda"
  functions = "lambda/dist"

Kā redzat, tas ir tas pats direktorijs, kas definēts kā outDir laukā lambda/tsconfig.json

Šādi vajadzētu izskatīties jūsu lietotnes struktūrai (nu... daļai no tās ;))

lietotne
└───lambda
│ └────bundle
│ │ │ graphql.ts
│ tsconfig.json.ts
└───src
│ └───generated
│ │ │ │ graphql.ts
│ │ │ index.ts
│ │ │ model.ts
│ │ │ resolvers.ts
│ │ │ schemas.ts
│ server.ts
│
│ codegen.yml
│ graphql.schema.json
│ netlify.toml
│ nodemon.json
│ tsconfig.json

Pievienot bundle:lambda skriptu uz package.json

"skripti": {
    "build:lambda": "npm run bundle && tsc -p lambda/tsconfig.json",
    "bundle": "ncp ./src ./lambda/bundle",
    "codegen": "graphql-codegen --config ./codegen.yml",
    "start": "nodemon",
  },

Izvietošana

Mēģināsim izvietot mūsu lietotni, izmantojot Netlify.

  1. Piesakieties savā Netlify kontā,
  2. Savienojieties ar savu Github,
  3. Noklikšķiniet uz pogas ‘Jauna vietne no Git’,
  4. Izvēlieties atbilstošu repo,
  5. Iestatiet izveides komandu npm palaist build:lambda,
  6. Pievienot vides mainīgo (MONGODB_URI),
  7. Izvietojiet...

Un TAAADAAAAAA...

PageNotFound.png

Tas ir tāpēc, ka beigu punkts pēc noklusējuma nav http://page/ bet http://page/.netlify/functions/graphql.

withoutRedirect.png

Kā to novērst? Tas ir ļoti vienkārši. Vienkārši izveidojiet _redirects ar:

/ / /.netlify/functions/graphql 200!

Atkārtoti izvietojiet un pārbaudiet.

redirect.png

Ceru, ka jums tas patika! Jūtieties brīvi uzlabot un mainīt.

Lasīt vairāk:

Kā nenogalināt projektu ar sliktu kodēšanas praksi?

Tīmekļa lietotņu drošība. Target=”_blank” ievainojamība

Web lietojumprogrammu drošība - XSS ievainojamība

Saistītie raksti

Ilustrācija viedtālruņa veselības aprūpes lietotnei ar sirds ikonu un pieaugošo veselības diagrammu, kas apzīmēta ar The Codest logotipu, kurš pārstāv digitālās veselības un HealthTech risinājumus.
Programmatūras izstrāde

Veselības aprūpes programmatūra: Mārketinga programmatūra: veidi, izmantošanas gadījumi

Šodien veselības aprūpes organizāciju rīcībā esošie rīki vairs neatgādina papīra diagrammas, kas tika izmantotas pirms vairākiem gadu desmitiem. veselības aprūpes programmatūra tagad atbalsta veselības aprūpes sistēmas, pacientu aprūpi un mūsdienīgu veselības aprūpes sniegšanu klīniskajās un...

TĀKĀDĒJAIS
Abstrakta ilustrācija ar lejupejošu joslu diagrammu ar augošu bultiņu un zelta monētu, kas simbolizē izmaksu efektivitāti vai ietaupījumus. Augšējā kreisajā stūrī redzams The Codest logotips ar saukli "In Code We Trust" uz gaiši pelēka fona.
Programmatūras izstrāde

Kā paplašināt izstrādātāju komandu, nezaudējot produkta kvalitāti

Palielināt izstrādātāju komandu? Uzziniet, kā augt, nezaudējot produkta kvalitāti. Šajā rokasgrāmatā aplūkotas pazīmes, kas liecina, ka ir pienācis laiks paplašināt komandu, komandas struktūra, pieņemšana darbā, vadība un rīki, kā arī tas, kā The Codest var...

TĀKĀDĒJAIS
Programmatūras izstrāde

Uz nākotni noturīgu tīmekļa lietojumprogrammu veidošana: The Codest ekspertu komandas ieskats

Uzziniet, kā The Codest izceļas mērogojamu, interaktīvu tīmekļa lietojumprogrammu izveidē, izmantojot modernākās tehnoloģijas un nodrošinot viengabalainu lietotāja pieredzi visās platformās. Uzziniet, kā mūsu zināšanas veicina digitālo transformāciju un biznesa...

TĀKĀDĒJAIS
Programmatūras izstrāde

Top 10 Latvijā bāzēti programmatūras izstrādes uzņēmumi

Mūsu jaunākajā rakstā uzziniet vairāk par Latvijas labākajiem programmatūras izstrādes uzņēmumiem un to inovatīvajiem risinājumiem. Uzziniet, kā šie tehnoloģiju līderi var palīdzēt uzlabot jūsu biznesu.

thecodest
Uzņēmumu un mērogošanas risinājumi

Java programmatūras izstrādes pamati: A Guide to Outsourcing Successfully

Izpētiet šo būtisko rokasgrāmatu par veiksmīgu outsourcing Java programmatūras izstrādi, lai uzlabotu efektivitāti, piekļūtu speciālajām zināšanām un sekmīgi īstenotu projektus ar The Codest.

thecodest

Abonējiet mūsu zināšanu bāzi un saņemiet jaunāko informāciju par IT nozares pieredzi.

    Par mums

    The Codest - starptautisks programmatūras izstrādes uzņēmums ar tehnoloģiju centriem Polijā.

    Apvienotā Karaliste - Galvenā mītne

    • 303B birojs, 182-184 High Street North E6 2JA
      Londona, Anglija

    Polija - Vietējie tehnoloģiju centri

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Krakova
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšava, Polija

    The Codest

    • Sākums
    • Par mums
    • Pakalpojumi
    • Case Studies
    • Zināt, kā
    • Karjera
    • Vārdnīca

    Pakalpojumi

    • Tā Konsultatīvais dienests
    • Programmatūras izstrāde
    • Backend izstrāde
    • Frontend izveide
    • Staff Augmentation
    • Backend izstrādātāji
    • Mākoņa inženieri
    • Datu inženieri
    • Citi
    • QA inženieri

    Resursi

    • Fakti un mīti par sadarbību ar ārējo programmatūras izstrādes partneri
    • No ASV uz Eiropu: Kāpēc Amerikas jaunuzņēmumi nolemj pārcelties uz Eiropu?
    • Tehnoloģiju ārzonas attīstības centru salīdzinājums: Tech Offshore Eiropa (Polija), ASEAN (Filipīnas), Eirāzija (Turcija)
    • Kādi ir galvenie CTO un CIO izaicinājumi?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Autortiesības © 2026 The Codest. Visas tiesības aizsargātas.

    lvLatvian
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian es_ESSpanish nl_NLDutch etEstonian elGreek pt_PTPortuguese cs_CZCzech lt_LTLithuanian is_ISIcelandic lvLatvian