(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': data().getTime(),įvykis:'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 diegimas naudojant "Netlify" funkcijas - The Codest
The Codest
  • Apie mus
  • Paslaugos
    • Programinės įrangos kūrimas
      • Priekinės dalies kūrimas
      • Galinės dalies kūrimas
    • Staff Augmentation
      • Priekinės dalies kūrėjai
      • Atgalinės versijos kūrėjai
      • Duomenų inžinieriai
      • Debesų inžinieriai
      • QA inžinieriai
      • Kita
    • Patariamoji tarnyba
      • Auditas ir konsultacijos
  • Pramonės šakos
    • Fintech ir bankininkystė
    • E-commerce
    • Adtech
    • Sveikatos technologijos
    • Gamyba
    • Logistika
    • Automobiliai
    • IOT
  • Vertė už
    • CEO
    • CTO
    • Pristatymo vadybininkas
  • Mūsų komanda
  • Case Studies
  • Sužinokite, kaip
    • Tinklaraštis
    • Susitikimai
    • Interneto seminarai
    • Ištekliai
Karjera Susisiekite su mumis
  • Apie mus
  • Paslaugos
    • Programinės įrangos kūrimas
      • Priekinės dalies kūrimas
      • Galinės dalies kūrimas
    • Staff Augmentation
      • Priekinės dalies kūrėjai
      • Atgalinės versijos kūrėjai
      • Duomenų inžinieriai
      • Debesų inžinieriai
      • QA inžinieriai
      • Kita
    • Patariamoji tarnyba
      • Auditas ir konsultacijos
  • Vertė už
    • CEO
    • CTO
    • Pristatymo vadybininkas
  • Mūsų komanda
  • Case Studies
  • Sužinokite, kaip
    • Tinklaraštis
    • Susitikimai
    • Interneto seminarai
    • Ištekliai
Karjera Susisiekite su mumis
Atgal rodyklė GRĮŽTI ATGAL
2020-10-02
Programinės įrangos kūrimas

GraphQL/MongoDB API diegimas naudojant "Netlify" funkcijas

The Codest

Pawel Rybczynski

Software Engineer

Tikslai Pradinė sąranka Įdiegti priklausomybes Pradėkime Pirmiausia į pagrindinį katalogą pridėkite tsconfig.json: Dabar sukurkime src/server.ts, skirtą serverių įgyvendinimui. Tada pridėkite dvi funkcijas: vieną vietiniam serveriui, o kitą - lambda. Gerai, neturime jokių resolverių ar tipų apibrėžčių, todėl turime jas sukurti. Tarkime, kad iš pradžių norime [...]

Tikslai

  1. Sukonfigūruokite vietinį ir "lambda" serverius.
  2. Prijunkite abi prie "MongoDB".
  3. Įgyvendinkite pagrindinį autentifikavimą.
  4. Įdiegti be serverio veikiantį "Apollo GraphQL API su "Netlify".
  5. Naudokite Typescript.

Pradinė sąranka

npm init -y

Įdiegti priklausomybes

npm install --save mašinraštis graphql aws-lambda @types/aws-lambda

Pradėkime

Pirmiausia pridėkite tsconfig.json į pagrindinį katalogą:

 {
 "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"]].
 }

Dabar sukurkime src/server.ts serverių įgyvendinimui. Tada pridėkite dvi funkcijas: vieną - vietiniam serveriui, kitą - lambda.

// src/server.ts
import { ApolloServer as ApolloServerLambda } from "apollo-server-lambda";
importuoti { ApolloServer } iš "apollo-server";


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

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

eksportas { createLambdaServer, createLocalServer };

Gerai, neturime jokių skirstytuvų ar tipų apibrėžčių, todėl turime jas sukurti. Tarkime, kad iš pradžių norime kurti naudotojus ir gauti apie juos informaciją.

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

const userSchema = gql`
  tipas User {
    id: ID!
    email: el. paštas: email: String!
    vardas: String!
  }

  tipas Užklausa {
    user(id: ID!): Vartotojas!
  }

  tipas Mutacija {
    createUser(vardas: String!, el. paštas: String!, slaptažodis: String!): User!
  }
`;

Jei nesate su tuo susipažinę, Apolonas parengė labai gražią pamoką

Dabar sukurkime naudotojo sprendiklį su viena užklausa ir viena mutacija.

// src/resolvers.ts
const userResolver = {
  Užklausa: {
    user: async (parent, args, context, info) => {
      {...}
    },
  },
  Mutacija: {
    createUser: async (parent, args, context, info) => {
      {...}
    },
  },
};

Bet neturime jokių duomenų... Ištaisykime tai 😉

Nepamirškite į serverį importuoti tipo apibrėžtį ir skirstytuvą.

// src/server.ts
import { ApolloServer as ApolloServerLambda } from "apollo-server-lambda";
importuoti { ApolloServer } iš "apollo-server";

importuoti { typeDefs } iš "./schemas";
importuoti { resolvers } iš "./resolvers";

{...}

Prisijungimas prie "MongoDB" per "mongoose

Dabar pats laikas sukurti ryšį su mūsų duomenų baze. Šiuo konkrečiu atveju tai bus MongoDB. Tai nemokama ir lengvai prižiūrima duomenų bazė. Tačiau prieš tai įdiekime dar dvi priklausomybes:

npm install --save mongoose dotenv

Pirmasis žingsnis - sukurti naudotojo modelį.

// src/model.ts
import mongoose, { Document, Error, Schema } iš "mongoose";

eksportas tipas User = Document & {
  _id: string,
  email: string,
  vardas: string,
  password: string,
};

ištrinti mongoose.connection.models["User"];

const UserSchema: Schema = new Schema({
  email: {
    tipas: : String,
    privalomas: true,
    unikalus: true,
  },
  vardas: {
    tipas: vardas::: String,
    privaloma: true,
    minLength: 3,
    maxLength: 32,
  },
  slaptažodis: {
    tipas: Slaptažodis:: String,
    privalomas: true,
  },
});

export const userModel = mongoose.model  ("User", UserSchema);

Padarykite slaptažodžius saugesnius

Pirmiausia saugumas! Dabar apsaugokime savo slaptažodžius juos šifruodami.

npm install --save bcrypt @types/bcrypt

Dabar įgyvendinkite slaptažodžio apsaugą išankstinio tarpinės programinės įrangos grįžtamojo skambučio viduje. Pre-middleware funkcijos vykdomos viena po kitos, kai kiekviena tarpinė programinė įranga skambina toliau. Kad slaptažodžiai būtų saugūs, naudojame metodą, kuris generuoja druską ir hash atskirais funkcijų iškvietimais.

// src/model.ts
import bcrypt from "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();
    });
  });
});
{...}

Tada į UserSchema pridėkite metodą comparePasswords:

// 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);
  });
};
{...}

Ir, žinoma, pakeiskite naudotojo tipą.

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

eksportas tipas User = Document & {
  _id: string,
  email: string,
  vardas: string,
  password: string,
  comparePasswords: comparePasswordFunction,
};

Dabar galime užmegzti ryšį tarp serverių ir duomenų bazių. MONGODB_URI yra aplinkos kintamasis, kuriame pateikiama ryšio eilutė, reikalinga ryšiui sukurti. Ją galite gauti iš klasterio skydelio, prisijungę prie "MongoDB atlas" paskyros. Įdėkite jį į .env

// .env
MONGODB_URI = ....;

Visada nepamirškite pridėti šio failo prie .gitignore. Puiku! Dabar pridėkime funkciją, kuri leistų prisijungti prie db.

// src/server.ts
importuoti mongoose, { Connection } iš "mongoose";
{...}
leiskite 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;
};
{...}

Kontekstas yra objektas, kuris yra bendras visiems skirstytuvams. Kad jį suteiktume, mums tereikia į ApolloServer konstruktorių įtraukti konteksto inicializavimo funkciją. Padarykime tai.

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

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

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

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

Kaip matote, mes taip pat perduodame userModel per kontekstas. Dabar galime atnaujinti sprendiklį:

// resolvers.ts
const userResolver = {
  Užklausa: {
    user: async (_, { email, name, password }, { models: { userModel } }) => {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutacija: {
    createUser: async (_, { id }, { models: { userModel } }) => {
      const user = await userModel.create({ el. paštas, vardas, slaptažodis });
      return user;
    },
  },
};

Atrodo gražiai! Dabar sukurkite pagrindinį serverio egzempliorių:

// src/index.ts
importuoti { createLocalServer } iš "./server";
require("dotenv").config();

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

const server = createLocalServer();

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

Vietinio serverio paleidimas naudojant Nodemon

Paskutinis dalykas prieš paleidžiant, pridėkite nodemon.json

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

Pridėti scenarijų į package.json

"scenarijai": {
    "start": "nodemon"
  },

Ir bėkite!

npm startas

Tokią informaciją turėtumėte gauti terminalo viduje:

Serveris ir veikia adresu http://localhost:4000/

Jei taip, atidarykite http://localhost:4000/ naršyklėje.

Turėtų pasirodyti GraphQL žaidimų aikštelė. Sukurkime naują naudotoją!

createUser.png

Pažvelkite, kaip tai atrodo duomenų bazėje.

duomenų bazėJohnDoe.png

Šaunu! Viskas veikia puikiai!

Pabandykime gauti naudotojo informacijos.

userWithoutAuth.png

Ir pridėkite slaptažodžio lauką...

userPassword.png

Puiku! Mes gauname klaidą Negalima užklausti lauko "password" tipo "User".". Kaip galite patikrinti atgal, šio lauko nepridėjome naudotojo tipo apibrėžtyje. Jis ten yra specialiai. Neturėtume sudaryti galimybės užklausti bet kokį slaptažodį ar kitą slaptą duomenys.

Kitas dalykas... Galime gauti naudotojo duomenis be jokio autentifikavimo... tai nėra geras sprendimas. Turime tai ištaisyti.

Bet prieš tai...

Konfigūruoti "Codegen

Naudokime GraphQL kodas generatorių, kad gautumėte suderinamą bazinį tipą pagal mūsų schemą.

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

Sukurti codegen.yml

perrašyti: true
schema: "http://localhost:4000"
generuoja:
  ./src/generated/graphql.ts:
    įskiepiai:
      - "typescript"
      - "typescript-resolvers"
  ./graphql.schema.json:
    įskiepiai:
      - "introspection"

Pridėti codegen scenarijų į package.json

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

Tada, kai veikia vietinis serveris, paleiskite scenarijų:

npm paleisti codegen

Sėkmės atveju gausite pranešimą:

  √ Parsavimo konfigūracija
  √ Generuoti išėjimus

Jei gausite šią informaciją, turėtų pasirodyti du failai:

  • graphql.schema.json pagrindiniame kataloge
  • graphql.ts naujai sukurtame kelyje src/generated

Mus labiau domina antrasis. Jį atidarę pastebėsite gražią tipų struktūrą.

Dabar galime patobulinti savo sprendiklius:

// src/resolvers.ts
importuoti { Resolvers, Token, User } iš "./generated/graphql";

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): pažadas => {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutacija: {
    createUser: async (
      _,
      { el. paštas, vardas, slaptažodis },
      { models: { userModel } }
    ): pažadas => {
      const user = await userModel.create({
        email,
        name,
        password,
      });
      return user;
    },
  },
};

Autentiškumo nustatymas

Toliau nustatykime paprastą žetonu pagrįstą autentifikavimą.

npm install --save jsonwebtoken @types/jsonwebtoken

Sukurti checkAuth funkcija, skirta patikrinti, ar simbolis yra galiojantis. Rezultatą pridėsime prie konteksto, kad galėtume jį pasiekti per sprendiklius.

// 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,
        },
      };
    },
  });
}

Be to, mums reikia būdo tokiam ženklui sukurti. Geriausias būdas - įdiegti prisijungimo užklausą mūsų sprendiklyje.

// resolvers.ts
importuoti { Resolvers, Token, User } iš "./generated/graphql";

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): pažadas => {
      if (!auth) throw new AuthenticationError("Jūs nesate autentifikuotas");

      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
    login: async (
      _,
      { el. paštas, slaptažodis },
      { models: { userModel } }
    ): Pažadas => {
      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,
      });

      grąžinti { token };
    },
  },
  Mutacija: {
    createUser: async (
      _,
      { el. paštas, vardas, slaptažodis },
      { models: { userModel } }
    ): pažadas => {
      const user = await userModel.create({
        email,
        name,
        password,
      });
      return user;
    },
  },
};

Taip pat reikia atnaujinti naudotojo tipo apibrėžtis pagal žetono tipą ir prisijungimo užklausą.

tipas Ženklas {
    token: String!
  }

tipas Užklausa {
    user(id: ID!): Vartotojas!
    login(el. paštas: String!, slaptažodis: String!): Token!
  }

Dabar pabandykime gauti naudotoją be simbolio

noAuth.png

Gerai, veikia puikiai! Pabandykite prisijungti

token.png

Pabandykime dar kartą gauti naudotoją, bet šį kartą į antraštes pridėjus simbolį

withToken.png

Šaunu! O kas, jei nustatysime neteisingus įgaliojimus?

wrongEmail.png

Puiku!

Pasirengimas diegti

Paskutinis žingsnis: dislokuokite serverless api su Netlify!

Sukurti aplanką lambda pagrindiniame dir ir įdėkite du failus:

Pirmajame yra AWS tvarkytojas. Ji sukuria "ApolloServerLambda" serverio egzempliorių ir
tada atskleiskite tvarkyklę naudodami to egzemplioriaus createHandler.

// lambda/graphql.ts
importuoti { APIGatewayProxyEvent, Context } iš "aws-lambda";
importuoti { createLambdaServer } iš "????";

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);
  });
};

Apie tai galite paskaityti daugiau.

Antrasis yra tsconfig. Svarbi dalis yra outDir srityje.

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

Tačiau yra problema. Negalime naudoti /src dir, nes "Netlify" negali pasiekti už lambda aplanko ribų. Taigi turime jį susieti į paketą.

npm install --save ncp

Tai paketas, leidžiantis kopijuoti katalogą.

Pridėti paketas scenarijų į package.json

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

Dabar, jei paleisite npm paleisti paketą, galite san pamatyti, kad naujai sukurtame lambda/bundle dir turime visus failus iš src/.

Atnaujinti importo kelią viduje lambda/graphql.ts

// lambda/graphql.ts
importuoti { APIGatewayProxyEvent, Context } iš "aws-lambda";
importuoti { createLambdaServer } iš "./bundle/server";

{...}

Dabar galite pridėti lambda/bundle dir prie .gitignore

Diegimas naudojant "Netlify

Turime "Netlify" nurodyti, kokia yra kūrimo komanda ir kur yra mūsų funkcijos. Norėdami tai padaryti, sukurkime netlify.toml file:

// netlify.toml
[build]
  komanda = "npm run build:lambda"
  functions = "lambda/dist"

Kaip matote, tai yra tas pats katalogas, kuris apibrėžtas kaip outDir laukas lambda/tsconfig.json

Taip turėtų atrodyti jūsų programos struktūra (na... jos dalis ;))

programa
└───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

Pridėti paketas:lambda scenarijų į package.json

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

Įdiegti

Pabandykime įdiegti savo programą per "Netlify".

  1. Prisijunkite prie savo "Netlify" paskyros,
  2. Prisijunkite prie "Github",
  3. Spustelėkite mygtuką ‘Nauja svetainė iš ’Git",
  4. Pasirinkite tinkamą repą,
  5. Nustatykite kūrimo komandą npm run build:lambda,
  6. Pridėti aplinkos kintamąjį (MONGODB_URI),
  7. Įdiegti...

Ir TAAADAAAAAA...

pageNotFound.png

Taip yra todėl, kad pagal numatytuosius nustatymus galinis taškas yra ne http://page/ bet http://page/.netlify/functions/graphql.

withoutRedirect.png

Kaip tai ištaisyti? Tai labai paprasta. Tiesiog sukurkite _redirects su:

/ / /.netlify/functions/graphql 200!

Dar kartą dislokuokite ir patikrinkite.

redirect.png

Tikiuosi, kad jums patiko! Jauskitės laisvai tobulinti ir keisti.

Skaityti daugiau:

Kaip nesunaikinti projekto dėl blogos kodavimo praktikos?

Interneto programų saugumas. Target=”_blank” pažeidžiamumas

Žiniatinklio programos saugumas - XSS pažeidžiamumas

Susiję straipsniai

Išmaniojo telefono sveikatos priežiūros programėlės su širdies piktograma ir kylančia sveikatos diagrama, pažymėtos The Codest logotipu, iliustracija, vaizduojanti skaitmeninės sveikatos ir sveikatos technologijų sprendimus.
Programinės įrangos kūrimas

Sveikatos priežiūros programinė įranga: Sveikatos priežiūros paslaugos: tipai, naudojimo atvejai

Įrankiai, kuriais šiandien naudojasi sveikatos priežiūros organizacijos, nė iš tolo neprimena prieš kelis dešimtmečius naudotų popierinių kortelių. sveikatos priežiūros programinė įranga dabar padeda sveikatos sistemoms, pacientų priežiūrai ir šiuolaikiniam sveikatos priežiūros paslaugų teikimui klinikinėse ir...

GERIAUSIAS
Abstrakti mažėjančios stulpelinės diagramos su kylančia rodykle ir auksine moneta, simbolizuojančia ekonomiškumą arba taupymą, iliustracija. Viršutiniame kairiajame viršutiniame kampe pavaizduotas The Codest logotipas ir šūkis "In Code We Trust" šviesiai pilkame fone.
Programinės įrangos kūrimas

Kaip padidinti savo Dev komandą neprarandant produkto kokybės

Didinate savo kūrėjų komandą? Sužinokite, kaip augti neprarandant produkto kokybės. Šiame vadove aptariami ženklai, kad atėjo laikas didinti komandą, komandos struktūra, įdarbinimas, vadovavimas ir įrankiai - ir kaip The Codest gali...

GERIAUSIAS
Programinės įrangos kūrimas

Sukurkite ateičiai atsparias žiniatinklio programas: The Codest ekspertų komandos įžvalgos

Sužinokite, kaip The Codest puikiai kuria keičiamo dydžio interaktyvias žiniatinklio programas, naudodama pažangiausias technologijas ir užtikrindama vientisą naudotojų patirtį visose platformose. Sužinokite, kaip mūsų patirtis skatina skaitmeninę transformaciją ir verslo...

GERIAUSIAS
Programinės įrangos kūrimas

10 geriausių Latvijoje įsikūrusių programinės įrangos kūrimo įmonių

Naujausiame mūsų straipsnyje sužinokite apie geriausias Latvijos programinės įrangos kūrimo įmones ir jų inovatyvius sprendimus. Sužinokite, kaip šie technologijų lyderiai gali padėti pakelti jūsų verslo lygį.

thecodest
Įmonių ir didinimo sprendimai

"Java" programinės įrangos kūrimo pagrindai: A Guide to outsourcing Outsourcing Successfully

Išnagrinėkite šį esminį vadovą, kaip sėkmingai outsourcing "Java" programinę įrangą kurti, kad padidintumėte efektyvumą, įgytumėte patirties ir sėkmingai įgyvendintumėte projektus su The Codest.

thecodest

Prenumeruokite mūsų žinių bazę ir būkite nuolat informuoti apie IT sektoriaus patirtį.

    Apie mus

    The Codest - tarptautinė programinės įrangos kūrimo bendrovė, turinti technologijų centrus Lenkijoje.

    Jungtinė Karalystė - būstinė

    • 303B biuras, 182-184 High Street North E6 2JA
      Londonas, Anglija

    Lenkija - vietiniai technologijų centrai

    • Fabryczna biurų parkas, Aleja
      Pokoju 18, 31-564 Krokuva
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšuva, Lenkija

    The Codest

    • Pagrindinis
    • Apie mus
    • Paslaugos
    • Case Studies
    • Sužinokite, kaip
    • Karjera
    • Žodynas

    Paslaugos

    • Patariamoji tarnyba
    • Programinės įrangos kūrimas
    • Galinės dalies kūrimas
    • Priekinės dalies kūrimas
    • Staff Augmentation
    • Atgalinės versijos kūrėjai
    • Debesų inžinieriai
    • Duomenų inžinieriai
    • Kita
    • QA inžinieriai

    Ištekliai

    • Faktai ir mitai apie bendradarbiavimą su išoriniu programinės įrangos kūrimo partneriu
    • Iš JAV į Europą: Kodėl Amerikos startuoliai nusprendžia persikelti į Europą?
    • Technikos plėtros centrų užsienyje palyginimas: Tech Offshore Europa (Lenkija), ASEAN (Filipinai), Eurazija (Turkija)
    • Kokie yra svarbiausi CTO ir CIO iššūkiai?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Autorinės teisės © 2026 The Codest. Visos teisės saugomos.

    lt_LTLithuanian
    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 lvLatvian is_ISIcelandic lt_LTLithuanian