![](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F201f7kip%2Fproduction%2F9d73e2c79376991c2d5e88a3daeb2160eef2863f-1400x785.webp&w=3840&q=75)
Nysnø: Genererte API-klienter med OpenAPI - Slik gjør du livet ditt enklere som utvikler 😎
Er du lei av å bruke tid på å forstå API-er, finne ut hvordan de fungerer, hvilke data som må sendes inn, og hva du får tilbake? Er du lei av å manuelt tolke responser og skrive modeller/DTO-er som ofte ender opp med feil? 🤯
Da har jeg gode nyheter til deg! 🎉 Ved å bruke OpenAPI-standarden kan utviklere spesifisere hvordan et API fungerer og automatisk generere API-klienter som andre apper kan bruke. Dette gjør integrasjon med systemer til en drøm 💭 Og sånn har vi nå gjort det med alle Snøkam sine API-er.
Men hvordan fungerer det egentlig?
OpenAPI-spesifikasjon: Dette er hjertet av prosessen. Spesifikasjonen beskriver nøyaktig hvordan et API skal brukes — hvilke endepunkter som finnes, hvilke data som kan sendes, og hvordan responsene ser ut. Tenk på det som en detaljert bruksanvisning for API-et ditt, skrevet i et format som både mennesker og maskiner forstår.
Verktøy for API-generering: Med verktøy som Swagger Codegen eller OpenAPI Generator kan du automatisk generere en API-klient for en rekke programmeringsspråk basert på OpenAPI-spesifikasjonen. Uansett om du jobber i JavaScript, Python, Java, eller noe helt annet, kan du få en ferdigbygd klient skreddersydd for ditt språk. I Snøkam genererer vi klienter for TypeScript og C#, men kan enkelt utvide og inkludere andre språk.
Integrasjon i prosjektet: Når klienten er generert, installerer du den i prosjektet ditt som en hvilken som helst annen modul eller bibliotek. Dette gjør det utrolig enkelt å integrere API-et i koden din. Pretty sweet, ikke sant?
Fordeler med genererte API-klienter
Konsistens: Klientene er generert direkte fra API-ets spesifikasjon, noe som sikrer nøyaktighet og eliminerer misforståelser om hvordan API-et fungerer.
Hastighet: Du slipper å skrive klientkoden selv, noe som sparer tid — tid du kan bruke på å fokusere på mer kritiske oppgaver eller ta en ekstra kaffepause ☕
Pålitelighet: Automatiseringen reduserer risikoen for feil, siden klientene genereres direkte fra spesifikasjonen.
Enklere vedlikehold: Når API-et endres, er det lett å oppdatere klientene på tvers av apper.
Et eksempel fra Snøkam 🚀
Vi har nylig utviklet et API i Snøkam som betaler ut daglig lønn i Bitcoin 💰 Det kan du for øvrig lese mer om i denne posten på Linkedin. For å få det til å fungere, har vi laget et API med flere endepunkter, blant annet et som henter ut alle transaksjoner.
[Function("GetTransactions")][OpenApiOperation("GetTransactions", "Transaction")][OpenApiParameter("from", In = ParameterLocation.Query, Type = typeof(DateTime), Description = "From datetime")][OpenApiParameter("to", In = ParameterLocation.Query, Type = typeof(DateTime), Description = "To datetime")][OpenApiResponseWithBody(HttpStatusCode.OK, "application/json", typeof(List<Transaction>), Description = "List of transactions")][OpenApiSecurity("Implicit", SecuritySchemeType.OAuth2, Flows = typeof(ImplicitAuthFlow))]public async Task<IActionResult> GetTransactions([HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "v1.0/transactions")] HttpRequest req,DateTime? from,DateTime? to){var transactions = await coinbaseService.GetTransactions(from, to);return new OkObjectResult(transactions);}
Denne koden spesifiserer nøyaktig hvordan endepunktet fungerer, hvilke parametere det aksepterer, og hvilke data det returnerer. Denne informasjonen brukes til å generere OpenAPI-spesifikasjonen, som igjen lar oss automatisk generere klienter.
Når vi dytter kode-endringer til main
, starter en workflow i GitHub Actions som genererer nye klienter basert på versjonering med Semantic Release.
- name: Generate Typescript clientuses: openapi-generators/openapitools-generator-action@v1with:generator: typescript-fetchopenapi-url: ${{ inputs.openApiUrl }}command-args: |--additional-properties=npmName=${{ inputs.packageName }},npmVersion=${{ inputs.version }},supportsES6=true \--openapi-normalizer SET_TAGS_FOR_ALL_OPERATIONS=${{ inputs.clientName }} \--api-name-suffix=Client \--skip-validate-spec
Resultatet? To nye klienter:
Men hvordan ser det ut på klientsiden?
🚀 I C# kan en integrasjon se slik ut:
using Microsoft.Extensions.Logging;using Snokam.Client.Crypto.Api;using Snokam.Client.Crypto.Client;using Snokam.Client.Crypto.Client.Auth;using Snokam.Client.Crypto.Model;namespace Snokam.PowerOfficeFunction.Services;public class CryptoService{private readonly ICryptoClientAsync _cryptoClient;private ILogger<CryptoService> _logger;public CryptoService(ILogger<CryptoService> logger){_logger = logger;_cryptoClient = new CryptoClient(new Configuration{BasePath = BASE_URL,OAuthClientId = CLIENT_ID,OAuthClientSecret = CLIENT_SECRET,OAuthTokenUrl = $"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token",OAuthFlow = OAuthFlow.APPLICATION,OAuthScope = scope});}public async Task<TransactionSummary> GetCryptoSummary(string email, DateTime from, DateTime to){return await _cryptoClient.GetTransactionsSummaryAsync(email, from, to);}public async Task<List<EmployeeTransactionSummary>> GetCryptoSummaries(DateTime from, DateTime to){return await _cryptoClient.GetTransactionsSummariesAsync(from, to);}}
_cryptoClient vil inkludere funksjoner for alle de tilsvarende endepunktene på API-et, og vil hente dataen med oppgitt autentisering.
I Typescript har vi valgt å gjøre det litt mer komplisert. Har du hørt om React Query fra TanStack? 😊 React Query er et populært bibliotek for React-applikasjoner som forenkler håndtering av server-state. Det gir utviklere enkle hooks for å hente, cache, synkronisere og oppdatere data fra API-er på en enkel måte.
Med litt magi gjør vi om alle funksjonene i @snokam/crypto-client om til React Query hooks 🤯 😀
import { Configuration, CryptoClient } from "@snokam/crypto-client";import { useClientQuery } from "@snokam/react-query";export const useCryptoClient = () => {const { isAuthenticated, getToken } = useAuth();const configuration = new Configuration({basePath: `${process.env.NEXT_PUBLIC_CRYPTO_API_BASE_URL}`,...(isAuthenticated && {accessToken: () =>getToken([`${process.env.NEXT_PUBLIC_CRYPTO_API_SCOPE}`]),}),});return useMemo(() => new CryptoClient(configuration), [configuration]);};export const useCryptoQuery = () =>useClientQuery({name: "Crypto",client: useCryptoClient(),});
Og voila, plutselig har vi tilgjengelig hooks for hvert endepunkt i API-et vårt, som er en lek å bruke 🤓
const { useGetMyTransactionsSummaryQuery } = useCryptoQuery();const {data: cryptoSummary,isRefetching: cryptoSummaryRefetching,isLoading: cryptoSummaryLoading,error: cryptoSummaryError,} = useGetMyTransactionsSummaryQuery({ from: startOfMonth(new Date()), to: endOfMonth(new Date()) },{ refetchOnWindowFocus: true },);
Happy coding! 🎉🚀