7. Serverless & Edge
7. Serverless & Edge
Deze les bespreken we serverless en edge functies. We doen dit in eerste instantie door het lesvoorbeeld van les 3 te publiceren op Netlify. Verder bouwen we enkele eenvoudige serverless en edge functies, waarin we, onder meer, een proxy bouwen voor API's waarvan de key beveiligd moet worden of die niet via CORS aangesproken mogen worden.
Deze les bouwt voort op het lesvoorbeeld van les 3.
Startbestanden
Serverless functions
Ondanks de naam, gebruiker serverless functions nog steeds een server. Het is tenslotte niet mogelijk om te reageren op een actie van een gebruiker zonder dat er geluisterd wordt naar HTTP requests.
De naam serverless komt van twee karakteristieke eigenschappen van een serverless functie:
- De ontwikkelaar van de applicatie/functie moet geen server onderhouden of deployen.
- De functie staat achter een API-gateway die beheerd wordt door de cloud provider (AWS, Vercel, Netlify, ...). Op het moment dat de functie opgeroepen worden, start de API-gateway (waarvoor je niet betaald) een de functie uitvoert (waarvoor je wel betaald). De functie is dus serverless in de zin dat je zelf geen server schrijft.
Omdat functies enkel aangerekend worden als deze uitgevoerd worden, is het dus ideaal om goedkoop een snel iets te bouwen. Elke functie is klein en implementeert een stukje van de applicatie. Dit betekent dat een serverless functie ook een strikte tijdslimiet heeft, meestal 10 seconden. Als de functie niet binnen deze tijd uitgevoerd kan worden, wordt deze automatisch afgesloten en krijgt de gebruiker een foutmelding terug.
Serverless functions doen dus niets en kosten geen geld als deze niet gebruikt worden, maar als er een zware traffic spike is, kan de cloud provider eenvoudig duizenden instanties van de functie starten. Serverless functions zijn dus perfect schaalbaar. Als je dit vergelijkt met een klassieke server die voldoende CPU en RAM moet hebben om de zwaarste traffic spikes aan te kunnen is dit een duidelijk voordeel. Op een klassieke server, gaat als deze hardware verloren op minder drukke momenten, terwijl een serverless function op dat moment geen geld kost.
Alhoewel wij serverless functions schrijven via Node, is dit geen algemene vereiste. Via AWS Lambda kunnen serverless functions geschreven worden in JavaScript, Python & Go, via Vercel kan Go, Ruby of JavaScript gebruikt worden en op Netlify zijn JavaScript en Go beschikbaar. Buiten deze services zijn er nog verschillende andere beschikbaar die eventueel andere talen ondersteunen.
Edge functions
Edge functions zijn een specifiek type serverless functions. Een edge function heeft al dezelfde eigenschappen als een serverless function, maar wordt daarbovenop gedeployed op de edge. Dit betekent dat de functie zo dicht mogelijk bij de gebruiker gedeployed wordt, via een CDN (content delivery network).
Een serverless functie wordt gedeployed op een server is een bepaalde regio, zoals eu-west-1, us-east, ... Een edge functie daarentegen wordt gedeployed op verschillende edge locaties, via een CDN wordt de gebruiker dan naar een zo dicht mogelijke edge locatie gebracht.

Bron: https://aws.amazon.com/about-aws/global-infrastructure/regions_az/
Deno
We zullen enkele eenvoudige edge functions schrijven via Deno, een alternatieve JavaScript runtime.
In tegenstelling tot Node heeft Deno native ondersteuning voor TypeScript, JSX, linting, formatting, ... Via Deno is het dus mogelijk om een TypeScript applicatie uit te voeren zonder dat deze eerst getranspileerd moet worden naar JavaScript.
Een ander opmerkelijk verschil is dat Deno geen package manager, package.json of node_modules gebruikt. Libraries worden geïmporteerd via een URL, vanuit GitHub, CDN's, ...
Een Deno applicatie heeft standaard geen toegang tot dingen zoals het filesystem, de fetch api, environment variables, subprocessen, ... Elk van deze features moet individueel toegestaan worden via een vlag in het Deno commando.
Deno kan geïnstalleerd worden met onderstaande commando's. Gebruik bij voorkeur het winget commando, als dit niet werkt (als Windows te oud is), dan kan je terugvallen op het PowerShell commando dat op alle Windows machine's zou moeten werken. Nadat je Deno geïnstalleerd hebt moet je jouw terminal en IDE opnieuw opstarten voordat er support voor Deno code beschikbaar is.
Info
Zowel in WebStorm als in VSCode moet je nog plug-ins installeren om goede ondersteuning te krijgen voor Deno. WebStorm vraagt automatisch om deze plug-in te installeren zodra er Deno code gedetecteerd wordt, moest dit niet gebeuren, dan kan je de plug-in installeren via Settings > Plugins > Marketplace > Deno.
Waarschuwing
Activeer de Deno plugins enkel als je effectief Deno code bent aan het schrijven, anders zal alles zeer traag verlopen en zal je IDE slechte ondersteuning bieden voor TypeScript.
Nadat je de plugin geactiveerd hebt, moet je de IDE opnieuw opstarten.
winget install denoirm https://deno.land/install.ps1 | iexInfo
WebStorm detecteert normaal automatisch dat Deno gebruikt wordt, als dit toch niet zou gebeuren, dan kan je dit manueel activeren via Settings > Languages & Frameworks > Deno > Enable Deno support for this project.
Hello World
We beginnen met een eenvoudige hello-world functie te schrijven en deze te deployen via Netlify.
Voordat we een functie kunnen testen, moeten we Netlify configuratie toevoegen. Dit doen we door een netlify.toml file aan te maken in de root van het project.
Deze configuratiefile bestaat uit twee onderdelen, build en functions. We gebruiken hier volgende opties:
- build
- command: Geen build commando nodig want er is geen frontend. De functies worden apart gebuild. Voor een React project zou dit pnpm build zijn en voor een Ionic project ionic build.
- functions: De map waar de functies ingezet zullen worden.
- publish: De map met de output van het build commando/een statische website.
- functions
- node_bundler: De bundler die gebruikt wordt om de functies te compileren.
[build]
command = "# no build command"
functions = "netlify/functions"
publish = "public"
[functions]
node_bundler = "esbuild"Alhoewel de publish folder ingesteld is op public bestaat deze map nog niet in de startbestanden, ook de netlify/functions map bestaat nog niet. We maken deze alvast aan, de functions directory blijft voorlopig leeg, in de public map plaatsen we een heel eenvoudige HTML-file, dit is niet strikt noodzakelijk, we doen dit hier enkel ter illustratie.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Les 7: Voorbeeld</title>
</head>
<body>
<h1>Les 7: Serverless & Edge</h1>
</body>
</html>Om niet na elke wijziging de code te moeten pushen naar Netlify installeren we de Netlify CLI, hiermee kunnen we een volledige Netlify deploy simuleren op onze lokale machine. Tenslotte starten we de Netlify dev server (in de map van de startbestanden). Dit laatste commando moet opnieuw uitgevoerd worden als er een nieuwe functie toegevoegd wordt.
netlify devTenslotte voegen we nog een nieuwe NPM library toe die de definities voor de HandlerEvent en HandlerContext objecten bevat die gebruikt worden in een serverless function.
Nu alle nodige configuratie gedaan is, kunnen we beginnen met de eerste serverless function te schrijven.
Elke functie wordt afgezonderd in een aparte map binnen de /netlify/functions directory. Dit is niet vereist, maar maakt het wel duidelijker welke code bij elkaar hoort. De functie die we nu schrijven bestaat uit slechts één file, maar het volgende voorbeeld zal een volledige Express API bevatten en dus uit verschillende files bestaan. Let op, de naam van het bestand dat de functie definieert moet overeen komen met de naam van de map waarin de functie zich bevindt.
Een serverless function moet een constante met de naam handler exporteren als een named export. Dit wil zeggen dat we geen export default mogen gebruiken. De manier waarop we deze functie definiëren is compatibel met AWS Lambda.
De handler functie heeft twee argumenten, het eerste argument heeft het type HandlerEvent, het tweede argument heeft het type HandlerContext en bevat informatie over het request, zoals gebruikers informatie. We gebruiken deze tweede parameter niet in deze cursus.
Een serverless function moet op Netlify of AWS een object teruggeven dat minstens één property statusCode bevat. Daarnaast kan er ook een optionele body of verzameling van header mee terug gegeven worden. Merk op dat het nodig is om de Content-Type header manueel in te stellen (in Express was dit niet nodig.)
import {HandlerContext, HandlerEvent, HandlerResponse} from '@netlify/functions'
export const handler = async (event: HandlerEvent, context: HandlerContext): Promise<HandlerResponse> => {
return {
body: '<h1>Hello World!</h1>',
statusCode: 200,
headers: {
'content-type': 'text/html',
},
}
}Als we deze functie bekijken in de browser, via de url http://localhost:8888/.netlify/functions/hello, krijgen we het onderstaande te zien.

Express als een serverless function
De API's die we de voorbije lessen gebouwd hebben zijn geschreven als een Node applicatie en dus moeten de Express server en het Node process permanent aan staan om binnenkomende verzoeken te kunnen verwerken. Dit is natuurlijk is strijd met het concept van serverless functions.
Ondanks de tegenstrijdige filosofie, kunnen we een Express applicatie toch deployen als een serverless function. Via de serverless-http library wordt een Express server omgevormd tot een serverless function. Hier moet wel een kanttekening bij gemaakt worden, een function is normaal gezien een klein stuk code dat snel opgestart kan worden. Een Express applicatie daarentegen is een groter geheel. Omdat een Express API groter is, duurt het ook langer voordat deze opgestart is. Als er geen actieve functie is die herbruikt kan worden (omdat er recent een request geweest is), zal het dus langer duren voordat de gebruiker een antwoord krijgt. Dit kan oplopen tot in de duizenden milliseconden. Het is duidelijk dat je een grote/complexe API dus beter opsplitst in meerdere routes. Afhankelijk van je host kan je de API opsplitsen per root path (dus /campuses, /rooms, ...) of kan je nog dieper gaan en ook opsplitsen op basis van sub-paden.
Nu we de nodige libraries hebben, verhuizen we alle API-code uit de startbestanden naar /netlify/functions/api/. Merk op dat we nu ook de naam van het entry bestand moeten aanpassen van app.ts naar api.ts, anders registreert Netlify dit niet als een serverless function. Nu dat de server code verhuisd is naar de functions map, is de src map volledig leeg.
Er moeten enkele wijzigingen gebeuren binnen in api.ts voordat we deze kunnen gebruiken als een serverless function. Ten eerste moeten de poort variable en de code die de server start verwijderd worden, de file ziet er dan als volgt uit:
import express from 'express'
import index from './routes/index.js'
import {corsMiddleware} from './middleware/cors.js'
import mongoose from 'mongoose'
import {env} from 'process'
const api = express()
api.use(corsMiddleware)
api.use(express.json())
api.use(index)
const databaseUrl = env.DATABASE_URL
if (!databaseUrl) {
throw new Error('No DATABASE URL specified.')
}
const _ = await mongoose.connect(databaseUrl)Nu dat de overbodige code verwijderd is, kunnen we serverless-http library gebruiken om Express te starten als een serverless function. Merk op dat we nog steeds een handler moeten exporteren, net zoals we hierboven gedaan hebben in de hello functie. De serverless functie die geëxporteerd wordt uit serverless-http krijgt de Express app als argument en vormt deze om naar een handler functie.
import express from 'express'
import index from './routes/index.js'
import {corsMiddleware} from './middleware/cors.js'
import mongoose from 'mongoose'
import {env} from 'process'
import serverless from 'serverless-http'
const api = express()
api.use(corsMiddleware)
api.use(express.json())
api.use(index)
const databaseUrl = env.DATABASE_URL
if (!databaseUrl) {
throw new Error('No DATABASE URL specified.')
}
const _ = await mongoose.connect(databaseUrl)
export const handler = serverless(api)Als we vervolgens proberen om de functie te testen (netlify dev), dan zien we dat er een foutmelding uitgeprint wordt.
De reden dat bovenstaande code niet werkt en geen errors geeft heeft te maken met de verschillende build systemen die gebruikt worden. In de startbestanden is de configuratie gebruikt die we in les 1 besproken hebben. Hier hebben we TypeScript zo geconfigureerd dat top-level-awaits toegestaan zijn, het is echter niet mogelijk om deze configuratie ook te gebruiken voor Netlify functions. Zoals we in netlify.toml gedefinieerd hebben, wordt ESBuild gebruikt om de functies te compileren en ESBuild ondersteunt geen top-level-awaits en is ook niet van plan om dit in de toekomst te doen omdat dit een zeer complex probleem is en veel moeilijkheden met zich mee brengt.
Als oplossing moeten we het aanmaken van de Mongoose client afzonderen in een nieuwe asynchrone functie. Deze functie kan vervolgens opgeroepen worden in elke DAL-functie zodat we garanderen dat de client actief is. Natuurlijk willen we slechts één instantie van de client aanmaken. Om dit probleem op te lossen zonderen we de client af in globale variabele, de initialisatie functie geeft dan de reeds geïnitialiseerde client terug als die bestaat en maakt er anders een nieuwe aan.
Natuurlijk moet de lijn die de client initialiseert in de api.js ook nog verwijderd worden.
import mongoose, {Mongoose} from 'mongoose'
let mongooseClient: Mongoose
export const ensureMongooseClient = async (): Promise<Mongoose> => {
if (mongooseClient) {
return mongooseClient
}
const databaseUrl = process.env.DATABASE_URL
if (!databaseUrl) {
throw new Error('No DATABASE URL specified.')
}
mongooseClient = await mongoose.connect(databaseUrl)
return mongooseClient
}import {ensureMongooseClient} from './mongooseClient'
export const getAllMovies = async ({title, genres, actors, type}: GetAllMoviesParams): Promise<IMovie[]> => {
await ensureMongooseClient()
// Weggelaten in dit fragment.
}
export const createMovie = async (movie: IdOptional<IMovie>): Promise<IMovie> => {
await ensureMongooseClient()
return Movie.create({...movie})
}
export const getMovieById = async (id: string): Promise<IMovie | null> => {
await ensureMongooseClient()
return Movie.findById(id)
}
export const updateMovie = async (id: string, updatedMovie: Partial<IMovie>): Promise<IMovie | null> => {
await ensureMongooseClient()
// Weggelaten in dit fragment.
}
export const deleteMovie = async (id: string): Promise<void> => {
await ensureMongooseClient()
await Movie.findByIdAndDelete(id)
}import express from 'express'
import index from './routes/index.js'
import {corsMiddleware} from './middleware/cors.js'
import serverless from 'serverless-http'
const api = express()
api.use(corsMiddleware)
api.use(express.json())
api.use(index)
export const handler = serverless(api)Als we de function opnieuw opstarten, zijn alle foutmeldingen verdwenen. Er is echter nog één probleem. Als we het GET /movies endpoint proberen te bezoeken via http://localhost:8888/.netlify/functions/api/movies, krijgen we onderstaande foutmelding te zien.

Deze foutmelding wordt veroorzaakt door het feit dat de API uitgevoerd wordt als een Netlify function en dus in het sub-pad /.netlify/functions/api draait. We moeten rekening houden met dit sub-pad en de volledige Express app inladen als een kind van deze route.
import express from 'express'
import index from './routes/index.js'
import {corsMiddleware} from './middleware/cors.js'
import serverless from 'serverless-http'
const api = express()
api.use(corsMiddleware)
api.use(express.json())
api.use('/.netlify/functions/api', index)
export const handler = serverless(api)Na deze aanpassing werkt de route perfect.

Hello World (Edge)
Net zoals we hierboven gedaan hebben voor de serverless functions, bouwen we een edge function die de tekst "Hello World" teruggeeft.
Om een edge function te gebruiken moeten we opnieuw wat configuratie toevoegen aan netlify.toml. De eerste optie is de directory waarin de edge functies zich bevinden, vervolgens moeten we ook aangeven hoe een functie bereikbaar zal zijn. Voor deze tweede optie moeten twee parameters ingesteld worden, het pad waarop de functie beschikbaar is en de filename van de functie.
[build]
command = "# no build command"
functions = "netlify/functions"
edge_functions = "netlify/edge-functions"
publish = "public"
[functions]
node_bundler = "esbuild"
[[edge_functions]]
path = "/edge/hello"
function = "hello"Na deze configuratie kunnen we verder gaan met de implementatie van de functie. Merk op dat we hiervoor de types importeren vanuit een URL, we moeten geen npm library installeren. Verder is er opnieuw één export in de file, dit keer moet het een default export zijn.
import type { EdgeFunction } from 'https://edge.netlify.com'
const handler: EdgeFunction = () => {
return new Response(
'<h1>Hello world</h1>',
{
headers: {
'content-type': 'text/html',
},
status: 200,
},
)
}
export default handlerBoeken in project Gutenberg op basis van locatie
De volgende functie die we schrijven zal een gelokaliseerde lijst van boeken ophalen uit Project Gutenberg. De lokalisatie gebeurd op basis van de locatie van de gebruiker. Zoals eerder besproken, worden edge functies verspreid over verschillende servers die zo dicht mogelijk bij de gebruiker staan. Hierdoor is er voor elk request een geo-object beschikbaar dat informatie bevat over het land van de gebruiker, en meestal nog specifiekere info zoals provincies of zelf geografische coördinaten.
De taal van de gebruiker is niet beschikbaar in het geo-object, maar we kunnen wel een NPM-library gebruiken om deze informatie op te halen. Zoals eerder besproken, importeert Deno libraries via een URL, niet via een [pnpm | yarn | npm] install. Om een library uit het npm registry te gebruiken, gebruiken we de naam van de library, optioneel met een versienummer, en voegen daar de prefix "npm:" aan toe. Alhoewel dit geldige Deno code is, wordt dit nog niet ondersteund door de backend waarop de meeste Deno functions gedeployed worden, daarom gebruiken we in de plaats van de "npm:" prefix het esm.sh CDN.
Onderstaande code kan gebruikt worden om in Deno een functie te definiëren die, gegeven de naam van een land, de officiële taal (de library werkt niet correct als er meerdere officiële talen zijn).
//import clm from 'npm:country-locale-map'
import clm from 'https://esm.sh/country-locale-map'
const getLanguageForCountry = (country: string): string | undefined => {
return clm.getLocaleByAlpha2(country)?.split('_')?.at(0)
}Nu dat deze functie geschreven is, kunnen we deze gebruiken om de boeken op the halen via gutendex.com.
Merk op dat het Request type in onderstaande code geen import vereist omdat dit een doodgewoon HTTP request is, waarvoor TypeScript een type ingebouwd heeft. Daarnaast zijn de Request en Context types overbodig in onderstaand voorbeeld, het EdgeFunction type bevat dezelfde informatie. De parameters hebben hier enkel een type gekregen ter illustratie.
import type {EdgeFunction, Context } from 'https://edge.netlify.com'
const handler: EdgeFunction = async (_req: Request, context: Context) => {
const language = getLanguageForCountry(context.geo.country?.code ?? '')
let url = 'http://gutendex.com/books'
if (language) {
url += `?languages=${language}`
}
const books = await fetch(url).then(res => res.json())
return new Response(
JSON.stringify(books),
{
headers: {
'content-type': 'application/json',
},
status: 200,
},
)
}
export default handler[build]
command = "# no build command"
functions = "netlify/functions"
edge_functions = "netlify/edge-functions"
publish = "public"
[functions]
node_bundler = "esbuild"
[[edge_functions]]
path = "/edge/hello"
function = "hello"
[[edge_functions]]
path = "/edge/geo"
function = "geo"Om bovenstaande code te testen kan de Netlify dev server gestart worden met de geo en mock vlaggen, op deze manier wordt de locatie van de dev server overschreven. Onderstaand commando past de locatie dus aan naar Finland.
netlify dev --geo=mock --country=FIAls we dan het resultaat uitlezen, krijgen we het volgende te zien.

Proxy
Als laatste voorbeeld schrijven we een proxy functie die requests doorstuurt naar de API-functie die we eerder in deze les gebouwd hebben. Natuurlijk heeft dit geen nut, maar het demonstreert hoe we dezelfde werkwijze kunnen toepassen om een API die geen CORS-requests toestaat aan te spreken vanuit een website. Natuurlijk moeten er dan wel CORS-headers meegestuurd worden (zoals besproken in les 1).
We beginnen met de URL waarvoor we de proxy schrijven in een variabele te plaatsen.
Het is natuurlijk mogelijk dat deze URL een navigatie-parameter bevat (om een specifieke film uit te lezen of aan te passen). Het is vanzelfsprekend dat deze parameter ook toegevoegd moet worden aan de URL waarnaar we in edge functie een request zullen sturen. Dit kan echter niet via de netlify.toml file, maar moet via een config object gebeuren. Toch moet de configuratie ook in het toml bestand geplaatst worden, anders registreert netlify de functie niet tijdens een deploy. We geven aan de path property van het config-object een array mee, elke waarde in deze array is een pad waarmee de functie bezocht kan worden. Een parameter kan op exact dezelfde manier toegevoegd worden als in een Express api, via een dubbelpunt.
Eens de parameter toegevoegd is in het pad, kunnen we via de het context object de parameter uitlezen en eventueel toevoegen aan de request-url.
import type {Config, EdgeFunction} from 'https://edge.netlify.com'
const handler: EdgeFunction = async (req, context) => {
let request = 'http://localhost:8888/.netlify/functions/api/movies'
if (context.params.id) {
request += `/${context.params.id}`
}
}
export default handler
export const config: Config = {path: ['/edge/proxy', '/edge/proxy/:id']}Naast een navigatie-parameter kunnen er ook query-parameters toegevoegd worden. Om deze uit te lezen bouwen we een nieuw URL-object op basis van de URL-string in het request. Uit dit object kunnen we vervolgens de query-parameters uitlezen en deze toevoegen aan de request-URL.
const handler: EdgeFunction = async (req, context) => {
let request = 'http://localhost:8888/.netlify/functions/api/movies'
let result = null
if (context.params.id) {
request += `/${context.params.id}`
}
request += '?'
const queryParams = new URL(req.url).searchParams
for (const [name, value] of queryParams) {
request += `${name}=${value}&`
}
}De URL van de edge functie is nu volledig omgezet naar een URL voor de serverless functie. We kunnen deze URL nu gebruiken om een fetch request te sturen naar de serverless API. Voor dit request kopiëren we de methode, headers en body van het originele request.
Eens we het antwoord van het fetch request krijgen, kunnen we het converteren naar JSON en doorsturen naar de client.
const handler: EdgeFunction = async (req, context) => {
let request = 'http://localhost:8888/.netlify/functions/api/movies'
let result = null
if (context.params.id) {
request += `/${context.params.id}`
}
request += '?'
const queryParams = new URL(req.url).searchParams
for (const [name, value] of queryParams) {
request += `${name}=${value}`
}
const proxyResult = await fetch(
request,
{
method: req.method,
headers: req.headers,
body: req.body,
},
)
if (req.method !== 'DELETE') {
result = await proxyResult.json()
}
return new Response(
JSON.stringify(result),
{
headers: {
'content-type': 'application/json',
},
status: proxyResult.status,
statusText: proxyResult.statusText,
},
)
}Voorbeeldcode
Volledig uitgewerkt lesvoorbeeld met commentaar
Appendix: Hosten via Netlify
Een applicatie heeft pas nut als deze gehost kan worden. Een lokaal project moet gelinkt worden met een Netlify site, dit kan via de CLI of via een git repository. Het linken via een git repository wijst zichzelf uit op de Netlify website en wordt verder niet besproken.
Voor we iets kunnen pushen naar Netlify, moeten we inloggen. Dit kan met onderstaand commando.
netlify loginNadat je ingelogd bent, kan het deploy commando gebruiken om een nieuwe site aan te maken op Netlify of om je project te linken met een bestaande site.
netlify deployEens je een site gekozen hebt, worden de inhoud van de public map, de gecompileerde edge en serverless functions gekopieerd naar Netlify.
Environment variables
Environment variables worden doorgaans niet bewaard in git en worden ook niet standaard meegestuurd met elke deploy. Toch moeten deze variabelen beschikbaar zijn op de server. Via de netlify CLI kan een lokale .env file naar Netlify gepusht worden.
Voor we iets kunnen pushen naar Netlify, moeten we inloggen. Dit kan met onderstaand commando.
netlify env:import .envWaarschuwing
Het is vanzelfsprekend dat een lokale MongoDB instantie niet bereikbaar is vanaf de Netlify of Deno servers.
Pas de DATABASE_URL variabele dus eerst aan naar een MongoDB Atlas database zoals beschreven is in de appendix van les 3.
Prisma & Netlify
Het is mogelijk om een Prisma applicatie te hosten op Netlify, maar de vereiste stappen zijn heel wat complexer dan diegene die we hierboven beschreven hebben voor een Mongoose applicatie.
Prisma vertoond (op het moment van schrijven) heel wat problemen waarmee rekening gehouden moet worden als je met Netlify wil deployen:
- De laatste versie van Prisma (5.7.0) werkt niet met pnpm als je de code schrijft in een serverless function.
- Om te deployen, moet npm gebruikt worden, maar met npm zijn ook enkele problemen:
- npm werkt niet met
netlify devop een Windows machine, terwijl builds (netlify deploy) wel werken. - Op een linux machine werkt
netlify devwel met npm.
- npm werkt niet met
Zoals hierboven beschreven is, heb je twee opties tijdens het ontwikkelen van een Prisma API met Netlify.
- Je gebruikt een versie < 5.7.0 en pnpm om te ontwikkelen. Als je wilt deployen verwijder je de Nodemodules_ map en voeg je vervolgens de commando's
npm install; npx prisma generate; netlify deployuit (of je build je applicatie op Netlify met npm).
Generators
Prisma gebruikt niet enkel JavaScript om database requests te maken, maar gebruikt een binaire module om verbinding te maken met de database. Deze binaire module moet natuurlijk ook beschikbaar zijn op Netlify, hier stoten we echter tegen het probleem dat Netlify geen gebruik maakt van Windows maar van een linux variant. Om de juiste binaire bestanden te kunnen uploaden in een deploy, moeten we deze eerste genereren, daarom passen we de Prisma configuratie aan zodat deze zowel de client voor het besturingssysteem waarop ontwikkeld wordt als de client voor de uiteindelijke server download.
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "rhel-openssl-1.0.x"]
}Client kopiëren naar deploy
Om de gegenereerde client te kopiëren naar de uiteindelijke deploy moeten we de configuratie in netlify.toml aanpassen. Via de externalNodemodules kunnen we aangeven welke modules gekopiëerd moeten worden naar de Netlify servers.
[build]
command = "# no build command"
functions = "netlify/functions"
publish = "public"
[functions]
node_bundler = "esbuild"
externalNodemodules = ["@prisma/client"]Connection string
Alhoewel we de connectionstring niet moeten aanpassen, doen we dit best wel. Standaard opent elke instantie van de Prisma client 3 verbindingen met de database, omdat er tijdens een traffic spike zeer veel verschillende functies tegelijkertijd actief kunnen zijn en omdat een gepauzeerde functie de connecties open houd, is het best om elke instantie te beperken tot 1 connectie. De database staat ook maar een beperkt aantal verbindingen toe, dus als we deze beperking niet implementeren is de kan groot dat het maximale aantal connecties bereikt wordt.
Merk op dat we hieronder de connection_limit toegevoegd hebben.
postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public&connection_limit=1Database
Het is vanzelfsprekend dat een lokale database niet gebruikt kan worden in combinatie met serverless functions. Als alternatief kunnen we een cloud database gebruiken die gehost wordt op Supabase.
Eens je project aangemaakt is, wordt het dashboard geladen. Selecteer hier de instellingen.

Navigeer vervolgens naar de database-instellingen en kopieer hier de connection string voor Node.js. Vergeet niet om je wachtwoord aan te passen en de eerder besproken connection_limit parameter toe te voegen.
