window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = Fenster if (w.LeadBooster) { console.warn('LeadBooster existiert bereits') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Bereitstellung von GraphQL/MongoDB API mit Netlify-Funktionen - The Codest
Der Codest
  • Über uns
  • Dienstleistungen
    • Software-Entwicklung
      • Frontend-Softwareentwicklung
      • Backend-Softwareentwicklung
    • Staff Augmentation
      • Frontend-Entwickler
      • Backend-Entwickler
      • Daten-Ingenieure
      • Cloud-Ingenieure
      • QS-Ingenieure
      • Andere
    • IT-Beratung
      • Prüfung und Beratung
  • Branchen
    • Fintech & Bankwesen
    • E-commerce
    • Adtech
    • Gesundheitstechnik
    • Herstellung
    • Logistik
    • Automobilindustrie
    • IOT
  • Wert für
    • CEO
    • CTO
    • Delivery Manager
  • Unser Team
  • Fallstudien
  • Gewusst wie
    • Blog
    • Begegnungen
    • Webinare
    • Ressourcen
Karriere Kontakt aufnehmen
  • Über uns
  • Dienstleistungen
    • Software-Entwicklung
      • Frontend-Softwareentwicklung
      • Backend-Softwareentwicklung
    • Staff Augmentation
      • Frontend-Entwickler
      • Backend-Entwickler
      • Daten-Ingenieure
      • Cloud-Ingenieure
      • QS-Ingenieure
      • Andere
    • IT-Beratung
      • Prüfung und Beratung
  • Wert für
    • CEO
    • CTO
    • Delivery Manager
  • Unser Team
  • Fallstudien
  • Gewusst wie
    • Blog
    • Begegnungen
    • Webinare
    • Ressourcen
Karriere Kontakt aufnehmen
Pfeil zurück ZURÜCK
2021-05-13
Software-Entwicklung

Einsatz von GraphQL/MongoDB API mit Netlify-Funktionen

Der Codest

Pawel Rybczynski

Software Engineer

Ziele Ersteinrichtung Installieren von Abhängigkeiten Fangen wir an. Fügen Sie zunächst die tsconfig.json zum Hauptverzeichnis hinzu: Nun erstellen wir src/server.ts für die Server-Implementierungen. Fügen Sie dann zwei Funktionen hinzu: eine für den lokalen Server und die zweite für Lambda. OK, wir haben keine Resolver oder Typdefinitionen, also müssen wir welche erstellen. Nehmen wir an, dass wir zunächst [...]

Ziele

  1. Konfigurieren Sie sowohl lokale als auch Lambda-Server.
  2. Verbinden Sie beide mit MongoDB.
  3. Implementieren Sie die Basisauthentifizierung.
  4. Einsatz von serverlosem Apollo GraphQL API mit Netlify.
  5. Typescript verwenden.

Ersteinrichtung

npm init -y

Abhängigkeiten installieren

npm install --save typescript graphql aws-lambda @types/aws-lambda

Fangen wir an

Fügen Sie zunächst die tsconfig.json zum Hauptverzeichnis:

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

Erstellen wir nun src/server.ts für Server-Implementierungen. Fügen Sie dann zwei Funktionen hinzu: eine für den lokalen Server und die zweite für Lambda.

// src/server.ts
importiere { ApolloServer as ApolloServerLambda } aus "apollo-server-lambda";
importieren { ApolloServer } von "apollo-server";


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

const createLocalServer = () =>
  new ApolloServer({
    typeDefs,
    Auflöser,
    introspection: true,
    Spielplatz: true,
    },
  });

exportieren { createLambdaServer, createLocalServer };

OK, wir haben keine Resolver oder Typdefinitionen, also müssen wir welche erstellen. Nehmen wir an, dass wir zunächst Benutzer anlegen und Informationen über sie erhalten wollen.

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

const userSchema = gql`
  type Benutzer {
    id: ID!
    email: String!
    name: String!
  }

  type Abfrage {
    user(id: ID!): User!
  }

  type Mutation {
    createUser(name: String!, email: String!, password: String!): User!
  }
`;

Falls Sie damit nicht vertraut sind, Apollo hat ein sehr schönes Tutorial vorbereitet

Nun wollen wir einen Benutzerauflöser mit einer Abfrage und einer Mutation erstellen.

// src/resolvers.ts
const userResolver = {
  Abfrage: {
    user: async (parent, args, context, info) => {
      {...}
    },
  },
  Mutation: {
    createUser: async (parent, args, context, info) => {
      {...}
    },
  },
};

Aber wir haben keine Daten... Das müssen wir ändern 😉 .

Vergessen Sie nicht, die Typdefinition und den Resolver auf den Server zu importieren.

// src/server.ts
importiere { ApolloServer as ApolloServerLambda } aus "apollo-server-lambda";
importieren { ApolloServer } von "apollo-server";

import { typeDefs } from "./schemas";
importieren { resolvers } aus "./resolvers";

{...}

Verbindung mit MongoDB über mongoose

Jetzt ist es an der Zeit, eine Verbindung mit unserer Datenbank herzustellen. In diesem speziellen Fall wird es MongoDB sein. Sie ist kostenlos und einfach zu pflegen. Aber vorher müssen wir noch zwei weitere Abhängigkeiten installieren:

npm install --save mongoose dotenv

Der erste Schritt besteht darin, ein Benutzermodell zu erstellen.

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

export type User = Document & {
  _id: string,
  email: string,
  name: string,
  Passwort: string,
};

delete mongoose.connection.models["User"];

const UserSchema: Schema = new Schema({
  email: {
    Typ: String,
    required: true,
    einzigartig: true,
  },
  name: {
    Typ: String,
    erforderlich: true,
    minLength: 3,
    maxLength: 32,
  },
  Kennwort: {
    Typ: String,
    erforderlich: true,
  },
});

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

Passwörter sicherer machen

Sicherheit geht vor! Sichern wir nun unsere Kennwörter, indem wir sie verschlüsseln.

npm install --save bcrypt @types/bcrypt

Implementieren Sie nun die Passwortsicherheit innerhalb des Pre-Middleware-Callbacks. Die Pre-Middleware-Funktionen werden nacheinander ausgeführt, wenn jede Middleware den nächsten Aufruf tätigt. Um die Kennwörter sicher zu machen, verwenden wir eine Technik, die ein Salt und einen Hash bei separaten Funktionsaufrufen erzeugt.

// 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);
      benutzer.passwort = hash;
      next();
    });
  });
});
{...}

Fügen Sie dann die Methode comparePasswords zu UserSchema hinzu:

// src/model.ts
{...}
UserSchema.methods.comparePasswords = function (
  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);
  });
};
{...}

Und natürlich, ändern Sie den Benutzertyp.

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

export type User = Document & {
  _id: string,
  email: string,
  name: string,
  Passwort: string,
  comparePasswords: comparePasswordFunction,
};

Jetzt können wir eine Verbindung zwischen Servern und Datenbanken herstellen. MONGODB_URI ist eine Umgebungsvariable, die einen Verbindungsstring enthält, der für die Erstellung der Verbindung benötigt wird. Sie können sie von Ihrem Cluster-Panel abrufen, nachdem Sie sich bei Ihrem MongoDB-Atlas-Konto angemeldet haben. Setzen Sie sie in .env

// .env
MONGODB_URI = ...;

Denken Sie immer daran, diese Datei zu .gitignore. Großartig! Fügen wir nun eine Funktion hinzu, die eine Verbindung mit der Datenbank ermöglicht.

// src/server.ts
import mongoose, { Connection } from "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;
};
{...}

Der Kontext ist ein Objekt, das von allen Resolvern gemeinsam genutzt wird. Um ihn bereitzustellen, müssen wir nur eine Kontextinitialisierungsfunktion zum ApolloServer-Konstruktor hinzufügen. Tun wir es.

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

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

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

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

Wie Sie sehen können, werden wir auch die userModel über Kontext. Wir können nun den Resolver aktualisieren:

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

Sieht gut aus! Erstellen Sie nun eine einfache Serverinstanz:

// src/index.ts
importiere { createLocalServer } von "./server";
require("dotenv").config();

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

const server = createLocalServer();

server.listen(port).then(({ url }) => {
  console.log(`Server ir läuft auf ${url}`);
});

Starten des lokalen Servers mit Nodemon

Als Letztes vor der Ausführung fügen Sie nodemon.json

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

Skript hinzufügen zu paket.json

"Skripte": {
    "start": "nodemon"
  },

Und laufen!

npm-Start

Sie sollten diese Informationen im Terminal erhalten:

Server ir läuft unter http://localhost:4000/

Wenn ja, öffnen Sie die http://localhost:4000/ innerhalb Ihres Browsers.

Der GraphQL-Spielplatz sollte erscheinen. Legen wir einen neuen Benutzer an!

createUser.png

Schauen Sie sich an, wie es in der Datenbank aussieht.

DatenbankJohnDoe.png

Super! Alles funktioniert bestens!

Versuchen wir, einige Benutzerinformationen zu erhalten.

userWithoutAuth.png

Und fügen Sie das Passwortfeld hinzu...

userPassword.png

Toll! Wir erhalten einen Fehler Abfrage des Feldes "Passwort" beim Typ "Benutzer" nicht möglich.. Wie Sie sehen können, haben wir dieses Feld nicht in die Benutzertypdefinition eingefügt. Es ist absichtlich dort. Es sollte nicht möglich sein, ein Passwort oder andere sensible Daten abzufragen.

Und noch etwas... Wir können Benutzerdaten ohne jegliche Authentifizierung abrufen... das ist keine gute Lösung. Wir müssen das in Ordnung bringen.

Aber vorher...

Codegen konfigurieren

Verwenden wir die GraphQL Code Generator, um einen kompatiblen Basistyp zu erhalten, der auf unserem Schema basiert.

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

erstellen. codegen.yml

Überschreiben: wahr
schema: "http://localhost:4000"
generiert:
  ./src/generated/graphql.ts:
    plugins:
      - "typescript"
      - "typescript-resolver"
  ./graphql.schema.json:
    plugins:
      - "introspection"

hinzufügen codegen Skript zu paket.json

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

Wenn der lokale Server läuft, führen Sie das Skript aus:

npm codegen ausführen

Im Erfolgsfall erhalten Sie eine Meldung:

  √ Konfiguration parsen
  √ Ausgänge generieren

Wenn Sie diese Information erhalten, sollten zwei Dateien erscheinen:

  • graphql.schema.json im Hauptverzeichnis
  • graphql.ts im neu erstellten Pfad src/erzeugt

Wir sind mehr an der zweiten interessiert. Wenn Sie es öffnen, werden Sie eine schöne Struktur von Typen feststellen.

Jetzt können wir unsere Resolver verbessern:

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

const userResolver: Resolvers = {
  Abfrage: {
    user: async (_, { id }, { models: { userModel }, auth }): Promise => {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutation: {
    createUser: async (
      _,
      { email, name, password },
      { models: { userModel } }
    ): Promise => {
      const user = await userModel.create({
        email,
        Name,
        Passwort,
      });
      return user;
    },
  },
};

Authentifizierung

Als Nächstes richten wir eine einfache tokenbasierte Authentifizierung ein.

npm install --save jsonwebtoken @types/jsonwebtoken

erstellen. checkAuth Funktion, um zu überprüfen, ob das Token gültig ist. Wir fügen das Ergebnis zum Kontext hinzu, damit wir innerhalb der Resolver darauf zugreifen können.

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

Außerdem brauchen wir eine Möglichkeit, ein solches Token zu erstellen. Der beste Weg ist die Implementierung einer Login-Abfrage innerhalb unseres Resolvers.

// resolvers.ts
import { Resolvers, Token, User } from "./generated/graphql";

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): Promise => {
      if (!auth) throw new AuthenticationError("Sie sind nicht authentifiziert");

      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
    login: async (
      _,
      { email, password },
      { models: { userModel } }
    ): Promise => {
      const user = await userModel.findOne({ email }).exec();

      if (!user) throw new AuthenticationError("Ungültige Anmeldedaten");

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

      if (!matchPasswords) throw new AuthenticationError("Ungültige Anmeldedaten");

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

      return { token };
    },
  },
  Mutation: {
    createUser: async (
      _,
      { email, name, password },
      { models: { userModel } }
    ): Promise => {
      const user = await userModel.create({
        email,
        Name,
        Passwort,
      });
      return user;
    },
  },
};

Sie müssen auch die Benutzertypdefinitionen nach Token-Typ und Anmeldeabfrage aktualisieren.

Typ Token {
    Token: String!
  }

type Abfrage {
    user(id: ID!): User!
    login(email: String!, password: String!): Token!
  }

Versuchen wir nun, den Benutzer ohne Token zu bekommen

noAuth.png

OK, funktioniert einwandfrei! Versuchen Sie, sich anzumelden

token.png

Versuchen wir erneut, den Benutzer abzurufen, aber dieses Mal mit einem Token in den Kopfzeilen

mitToken.png

Super! Und was, wenn wir falsche Anmeldedaten eingeben?

wrongEmail.png

Schön!

Einsatz vorbereiten

Letzter Schritt: Bereitstellung der serverlosen API mit Netlify!

Ordner erstellen lambda im Hauptverzeichnis und fügen Sie zwei Dateien ein:

Erste enthält AWS Handler. Er erstellt eine ApolloServerLambda-Serverinstanz und
dann einen Handler mit createHandler dieser Instanz ausstellen.

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

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

Sie können mehr darüber lesen.

Der zweite Teil ist die tsconfig. Ein wichtiger Teil ist die outDir Feld.

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

Aber es gibt ein Problem. Wir können nicht verwenden /src dir, weil Netlify nicht außerhalb des Lambda-Ordners zugreifen kann. Also müssen wir es bündeln.

npm install --save ncp

Es ist ein Paket, mit dem man Verzeichnisse kopieren kann.

hinzufügen Bündel Skript zu paket.json

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

Wenn Sie nun npm-Bündel ausführenkönnen Sie sehen, dass in neu erstellten lambda/bündeln dir haben wir alle Dateien von src/.

Aktualisieren Sie den Importpfad in lambda/graphql.ts

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

{...}

Sie können jetzt lambda/bundle dir zu .gitignore

Einsatz mit Netlify

Wir müssen Netlify mitteilen, was der Build-Befehl ist und wo sich unsere Funktionen befinden. Zu diesem Zweck erstellen wir netlify.toml file:

// netlify.toml
[build]
  Befehl = "npm run build:lambda"
  funktionen = "lambda/dist"

Wie Sie sehen können, handelt es sich um dasselbe Verzeichnis, das als outDir Feld in lambda/tsconfig.json

So sollte Ihre App-Struktur aussehen (naja... ein Teil davon ;))

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

hinzufügen bündel:lambda Skript zu paket.json

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

Bereitstellung von

Versuchen wir, unsere App über Netlify zu verteilen.

  1. Melden Sie sich bei Ihrem Netlify-Konto an,
  2. Verbinden Sie sich mit Ihrem Github,
  3. Klicken Sie auf die Schaltfläche "Neue Website aus Git",
  4. Wählen Sie ein geeignetes Repo,
  5. Setzen Sie den Build-Befehl npm run build:lambda,
  6. Umgebungsvariable (MONGODB_URI) hinzufügen,
  7. Einsetzen...

Und TAAADAAAA...

pageNotFound.png

Das liegt daran, dass der Endpunkt standardmäßig nicht der http://page/ sondern http://page/.netlify/functions/graphql.

withoutRedirect.png

Wie kann man das beheben? Das ist ganz einfach. Erstellen Sie einfach Umleitungen mit:

/ /.netlify/functions/graphql 200!

Erneut bereitstellen und überprüfen.

umleiten.png

Ich hoffe, es hat euch gefallen! Fühlen Sie sich frei zu verbessern und zu ändern.

Lesen Sie mehr:

Wie kann man ein Projekt nicht durch schlechte Programmierpraktiken zerstören?

Sicherheit von Webanwendungen. Target="_blank" Sicherheitslücke

Sicherheit von Webanwendungen - XSS-Schwachstelle

Ähnliche Artikel

Software-Entwicklung

Zukunftssichere Web-Apps bauen: Einblicke vom The Codest-Expertenteam

Entdecken Sie, wie sich The Codest bei der Erstellung skalierbarer, interaktiver Webanwendungen mit Spitzentechnologien auszeichnet, die nahtlose Benutzererfahrungen auf allen Plattformen bieten. Erfahren Sie, wie unsere Expertise die digitale Transformation und...

DAS SCHÖNSTE
Software-Entwicklung

Top 10 Softwareentwicklungsunternehmen in Lettland

Erfahren Sie in unserem neuesten Artikel mehr über die besten Softwareentwicklungsunternehmen Lettlands und ihre innovativen Lösungen. Entdecken Sie, wie diese Technologieführer Ihr Unternehmen voranbringen können.

thecodest
Enterprise & Scaleups Lösungen

Grundlagen der Java-Softwareentwicklung: Ein Leitfaden für erfolgreiches Outsourcing

Entdecken Sie diesen wichtigen Leitfaden zum erfolgreichen Outsourcing der Java-Softwareentwicklung, um die Effizienz zu steigern, auf Fachwissen zuzugreifen und den Projekterfolg mit The Codest voranzutreiben.

thecodest
Software-Entwicklung

Der ultimative Leitfaden für Outsourcing in Polen

Der Anstieg des Outsourcings in Polen wird durch wirtschaftliche, bildungspolitische und technologische Fortschritte angetrieben, die das IT-Wachstum und ein unternehmensfreundliches Klima fördern.

TheCodest
Enterprise & Scaleups Lösungen

Der vollständige Leitfaden für IT-Audit-Tools und -Techniken

IT-Audits gewährleisten sichere, effiziente und gesetzeskonforme Systeme. Erfahren Sie mehr über ihre Bedeutung, indem Sie den vollständigen Artikel lesen.

Der Codest
Jakub Jakubowicz CTO & Mitbegründer

Abonnieren Sie unsere Wissensdatenbank und bleiben Sie auf dem Laufenden über das Fachwissen aus dem IT-Sektor.

    Über uns

    The Codest - Internationales Software-Unternehmen mit technischen Zentren in Polen.

    Vereinigtes Königreich - Hauptsitz

    • Büro 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokale Tech-Hubs

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polen

      Der Codest

    • Startseite
    • Über uns
    • Dienstleistungen
    • Fallstudien
    • Gewusst wie
    • Karriere
    • Wörterbuch

      Dienstleistungen

    • IT-Beratung
    • Software-Entwicklung
    • Backend-Softwareentwicklung
    • Frontend-Softwareentwicklung
    • Staff Augmentation
    • Backend-Entwickler
    • Cloud-Ingenieure
    • Daten-Ingenieure
    • Andere
    • QS-Ingenieure

      Ressourcen

    • Fakten und Mythen über die Zusammenarbeit mit einem externen Softwareentwicklungspartner
    • Aus den USA nach Europa: Warum entscheiden sich amerikanische Start-ups für eine Verlagerung nach Europa?
    • Tech Offshore Development Hubs im Vergleich: Tech Offshore Europa (Polen), ASEAN (Philippinen), Eurasien (Türkei)
    • Was sind die größten Herausforderungen für CTOs und CIOs?
    • Der Codest
    • Der Codest
    • Der Codest
    • Privacy policy
    • Website terms of use

    Urheberrecht © 2025 von The Codest. Alle Rechte vorbehalten.

    de_DEGerman
    en_USEnglish sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek de_DEGerman