Distribuire un'API GraphQL come funzione di Azure

Questo articolo illustra come distribuire un'API GraphQL in Azure in una funzione di Azure.

Questo esempio illustra l'uso del server Apollo in una funzione di Azure per ricevere una query GraphQL e restituire il risultato.

{
    hello
}

Il server risponde con JSON:

{
    "hello": "Hello from GraphQL backend"
}

Preparare l'ambiente di sviluppo

Assicurarsi che nella workstation per sviluppatori locale siano installati gli elementi seguenti:

Clonare ed eseguire il codice di esempio GraphQL della funzione di Azure

  1. Aprire una finestra del cd terminale e nella directory in cui si vuole clonare il codice di esempio.

  2. Clonare il repository di codice di esempio:

    git clone https://github.com/Azure-Samples/js-e2e-azure-function-graphql-hello.git
    
  3. Aprire la directory in Visual Studio Code:

    cd js-e2e-azure-function-graphql-hello && code .
    
  4. Installare le dipendenze del progetto:

    npm install
    
  5. Eseguire l'esempio:

    npm start
    

Eseguire query sulla funzione di Azure locale con GraphQL usando GraphQL playground

Il pacchetto npm apollo-server-azure-functions include un playground GraphQL che è possibile usare per testare l'API GraphQL. Usare questo ambiente per testare l'API GraphQL in locale.

  1. Aprire il browser alla pagina http://localhost:7071/api/graphql

  2. Immettere una query {hello}

  3. Visualizzare la risposta {"data":{"hello":"Hello from GraphQL backend"}}

    Screenshot del browser che mostra l'area giochi GraphQL ospitata da un'API per le funzioni di Azure

Eseguire query sulla funzione di Azure con GraphQL usando cURL

  1. In VS Code aprire un terminale integrato.

  2. Immettere il comando cURL:

    curl 'http://localhost:7071/api/graphql' \
         -H 'content-type: application/json' \
         --data-raw '{"query":"{hello}"}' 
    
  3. Visualizzare la risposta {"data":{"hello":"Hello from GraphQL backend"}}

Creare la risorsa funzione di Azure da VS Code

  1. In VS Code selezionare Esplora azure.

  2. Nella sezione Funzioni di Azure selezionare la sottoscrizione di Azure in cui creare la risorsa funzione di Azure, quindi fare clic con il pulsante destro del mouse per selezionare Crea app per le funzioni in Azure.

  3. Completare le richieste:

    Prompt Immettere
    Immettere un nome univoco globale per la nuova app per le funzioni. Immettere un nome univoco, usato come sottodominio dell'URL, ad esempio YOURALIAS-azure-function-graphql-hello .
    Selezionare uno stack di runtime. Node.js 14 LTS
    Selezionare una posizione per le nuove risorse. Selezionare una località geografica vicina.

    VS Code notifica quando la distribuzione viene completata.

Distribuire l'API GraphQL da VS Code

  1. In VS Code, sempre in Azure Explorer, trovare la nuova risorsa funzione di Azure nella sottoscrizione.

  2. Fare clic con il pulsante destro del mouse sulla risorsa e scegliere Distribuisci nell'app per le funzioni.

  3. Selezionare la finestra di output dalla notifica per controllare la distribuzione.

    Al termine della distribuzione, passare alla sezione successiva.

Eseguire query nell'API GraphQL con cURL

  1. In VS Code aprire un terminale integrato.

  2. Modificare il comando cURL da usando la funzione locale alla funzione remove. Modificare l'URL nel comando seguente per usare l'URL della funzione di Azure:

    curl 'https://diberry-azure-function-graphql-hello.azurewebsites.net/api/graphql' \
         -H 'content-type: application/json' \
         --data-raw '{"query":"{hello}"}' |
    

    L'API risponde con:

    {"data":{"hello":"Hello from our GraphQL backend!"}}
    

Esaminare il codice

Il codice usato in questo articolo richiede il pacchetto npm apollo-server-azure-functions per risolvere la query GraphQL.

Il codice per questa query si trova nel ./graphql/index.ts file .

import { ApolloServer, gql} from "apollo-server-azure-functions";

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: () => "Hello from our GraphQL backend!",
  },
};
// @ts-ignore
const server = new ApolloServer({ typeDefs, resolvers, debug: true,playground: true});

export default server.createHandler({
  cors: {
    origin: '*'
  },
});

Le righe evidenziate sono descritte nella tabella seguente:

A linee Descrizione
const typeDefs = gql Definire lo schema GraphQL che l'API supporta.
const resolvers Definire i gestori per lo schema GraphQL (noti come resolver in GraphQL). hello, dallo schema, viene data una funzione di risoluzione per restituire dati dall'API: () => "Hello from our GraphQL backend!" .
const server = new ApolloServer({ typeDefs, resolvers }); Creare una versione di Funzione di Azure del server Apollo con typeDef, resolver e il campo giochi.

Risoluzione dei problemi relativi all'API graphql

Usare la guida alla risoluzione dei problemi seguente per risolvere eventuali problemi.

Problema Possibile correzione
Il comando cURL non restituisce alcun valore In VS Code espandere la risorsa Funzione di Azure in Esplora risorse di Azure. Nel nodo File verificare che tutti i file locali siano stati spostati nel percorso remoto e che /dist la cartella sia stata generata. Se i file non sono presenti, ridistribuire l'app e controllare l'output della distribuzione per eventuali errori. Se i file esistono, eseguire di nuovo il comando cURL, aggiungendo alla fine del comando per visualizzare il codice --verbose di stato restituito.
L'API non restituisce alcun valore, ma il codice è corretto. La funzione di Azure restituisce i risultati del server Apollo se indica ./graphql/function.json correttamente il nome dell'associazione restituita come $return . Se si è riprodotto con il file function.json, assicurarsi che il nome dell'associazione HTTP sia reimpostato sul valore di $return . Un altro possibile problema è che se è stato modificato , trovato anche nel file da a un altro valore, è necessario modificare il valore in o passare correttamente l'autenticazione quando si usa authLevelfunction.jsonanonymousanonymous l'API.

Si è verificato un problema non descritto nella tabella precedente? Aprire un problema da insodd dirci.

Progettare una seconda API per consentire la creazione, l'aggiornamento e la lettura degli endpoint API

Si consideri una nuova API con l'endpoint seguente per gestire i messaggi:

  • /api/mymessages/getMessage
  • /api/mymessages/getMessages
  • /api/mymessages/createMessage
  • /api/mymessages/updateMessage

Un singolo elemento dati ha un identifer, un contenuto e dati di autore univoci. Interno alla funzione di Azure, simile a

{"79e4c338-162d-4c1e-a6f0-320bd78a7817": {
        "author": "dina",
        "content": "good morning"
    }
}

Il sistema di risoluzione può rimodellare i dati restituiti dall'API in modo da avere un aspetto simile al seguente:

[
    {
        "id":"79e4c338-162d-4c1e-a6f0-320bd78a7817",
        "author": "dina",
        "content": "good morning"
    }
]

Se la query richiede solo determinati campi, ad esempio e , il server Apollo esegue l'eliminazione dei dati anziché del sistema di content risoluzione:

[
    {
        "id":"79e4c338-162d-4c1e-a6f0-320bd78a7817",
        "content": "good morning"
    }
]

La chiave è che il sistema di risoluzione può rimodellare i dati e il server Apollo può eliminare i dati in modo che corrispondano alla query.

Aggiungere la seconda API GraphQL alla funzione di Azure

  1. In VS Code selezionare Visualizza, quindi selezionare il riquadro comandi.

  2. Cercare e selezionare Funzioni di Azure: Crea funzione.

  3. Completare le richieste:

    Prompt Immettere
    Select a template for your function (Selezionare un modello per la funzione) Trigger HTTP
    Specificare un nome di funzione mymessages
    Autorizzazione Anonimo

Aggiungere il codice per la nuova API GraphQL

  1. In ./mymessages/index.ts sostituire l'intero file con il codice seguente:

    import { ApolloServer, gql} from "apollo-server-azure-functions";
    import { uuid } from 'uuidv4';
    
    const database = { [uuid()] :{"author": "dina", "content": "good morning"} };
    
    const typeDefs = gql`
        input MessageInput {
            content: String
            author: String
        }
    
        type Message {
            id: ID!
            content: String
            author: String
        }
    
        type Query {
            getMessage(id: ID!): Message,
            getMessages:[Message]
        }
    
        type Mutation {
            createMessage(input: MessageInput): Message
            updateMessage(id: ID!, input: MessageInput): Message
        }
    `;
    
    class Message {
    
        id: any;
        content: string;
        author: string;
    
        constructor(id: String, {content, author}) {
            this.id = id;
            this.content = content;
            this.author = author;
        }
    }
    
    const resolvers = {
        Mutation: {
            createMessage: (_, {input}) => {
                const id = uuid();
    
                database[id] = input;
                return new Message(id, input);
            },
            updateMessage: (_, {id, input}) => {
                if (!database[id]) {
                    throw new Error('no message exists with id ' + id);
                }
                database[id] = input;
                return new Message(id, input);
            },
        },
        Query: {
            getMessage: (_, {id}) => {
                if (!database[id]) {
                    throw new Error('no message exists with id ' + id);
                }
                return new Message(id, database[id]);
            },
            getMessages: (_, ) => {
                let arr = [];
                for (var key in database) {
                    if (database.hasOwnProperty(key)) {
                        arr.push({
                            id: key,
                            author: database[key].author,
                            content: database[key].content
                        });
                    }
                }
                return arr;
            },
        }
    };
    // @ts-ignore
    const server = new ApolloServer({ typeDefs, resolvers, playground: true});
    
    export default server.createHandler({
      cors: {
        origin: '*'
      },
    });
    
  2. Nel file modificare il secondo oggetto nome da usare in modo che il server Apollo possa restituire correttamente ./mymessages/function.json le funzionalità tramite la funzione di $return Azure:

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
  3. Nel terminale VS Code integrato compilare e avviare la funzione:

    npm run build && npm start
    
  4. Aprire l'area giochi in un http://localhost:7071/api/mymessages browser.

  5. Immettere ed eseguire ognuna delle query GraphQL seguenti nel campo giochi. Il valore visualizzato sarà diverso dai valori elencati di seguito id perché questi valori vengono generati in modo id casuale.

    Scopo Query Risultato
    Ottieni tutti i messaggi {getMessages{id, author, content}} {"data": {"getMessages": [{"id": "d8732ed5-26d8-4975-98a5-8923e320a77f","author": "dina", "content": "good morning"}]}}
    Aggiungere un messaggio mutation{createMessage(input:{author: "John Doe",content: "Oh happy day"}){id}} {"data": {"createMessage": {"id": "33febdf6-e618-4884-ae4d-90827280d2b2"}}}
    Ottenere un messaggio {getMessage(id: "33febdf6-e618-4884-ae4d-90827280d2b2"){id, content, author}} {"data": {"getMessage": {"id": "33febdf6-e618-4884-ae4d-90827280d2b2","content": "Oh happy day", "author": "John Doe"}}}
    Aggiornare un messaggio mutation{ updateMessage (id: "33febdf6-e618-4884-ae4d-90827280d2b2",input:{author: "John Doe", content: "another great day..." }){id, content, author}} {"data": {"updateMessage": {"id": "33febdf6-e618-4884-ae4d-90827280d2b2","content": "another great day...","author": "John Doe"}}}
  6. Per eseguire la distribuzione nella funzione di Azure basata sul cloud, ripetere i passaggi di distribuzione.

Passaggi successivi