Hızlı Çalışma
Hızlı Çalışma
Fastify, şemaya dayalı bir yaklaşım kullanır ve zorunlu olmamakla birlikte, rotalarınızı doğrulamak ve çıktılarınızı serileştirmek için JSON Schema kullanmanızı öneririz. İçerisinde, Fastify şemayı yüksek performanslı bir işlev haline getirir.
Doğrulama, içerik türü application-json
olduğu müddetçe yapılacaktır; bu, içerik türü ayrıştırıcı
belgesinde tanımlanmıştır.
Bu bölümdeki tüm örnekler, JSON Schema Taslak 7 spesifikasyonunu kullanmaktadır.
⚠ Güvenlik Uyarısı
Şema tanımını uygulama kodu olarak ele alın. Doğrulama ve serileştirme özellikleri dinamik olarak
new Function()
ile kod değerlendirir ki bu, kullanıcıdan sağlanan şemalarla kullanmak için güvenli değildir. Daha fazla bilgi için Ajv ve fast-json-stringify belgelerine bakın.Fastify tarafından desteklenen
$async
Ajv özelliği ilk doğrulama stratejisinin bir parçası olarak kullanılmamalıdır. Bu seçenek, Veritabanlarına erişmek için kullanılır ve doğrulama süreci sırasında bunları okumak, uygulamanıza Hizmet Reddi Saldırılarına yol açabilir.async
görevlerini çalıştırmanız gerekiyorsa, doğrulama tamamlandıktan sonraFastify'nin kancalarını
kullanın; örneğinpreHandler
.
Temel Kavramlar
Doğrulama ve serileştirme görevleri, iki farklı ve özelleştirilebilir aktör tarafından işlenir:
- Bir istek için doğrulama Ajv v8
- Bir yanıtın gövdesinin serileştirilmesi için fast-json-stringify
Bu iki ayrı varlık yalnızca Fastify'nin örneğine .addSchema(schema)
ile eklenen JSON şemalarını paylaşır.
Paylaşılan Bir Şemanın Eklenmesi
Paylaşılan şemalarla ilgili daha fazla bilgi
addSchema
API'si sayesinde, Fastify örneğine birden fazla şema ekleyebilir ve ardından bunları uygulamanızın farklı bölümlerinde yeniden kullanabilirsiniz. Her zamanki gibi, bu API kapsüllenmiştir.
Paylaşılan şemalar, JSON Şeması $ref
anahtar kelimesi aracılığıyla yeniden kullanılabilir. Referansların nasıl çalıştığına dair bir genel bakış:
myField: { $ref: '#foo' }
mevcut şemada$id: '#foo'
ile alan ararmyField: { $ref: '#/definitions/foo' }
mevcut şemadadefinitions.foo
alanını ararmyField: { $ref: 'http://url.com/sh.json#' }
$id: 'http://url.com/sh.json'
ile eklenen bir paylaşılan şemayı ararmyField: { $ref: 'http://url.com/sh.json#/definitions/foo' }
$id: 'http://url.com/sh.json'
ile eklenen bir paylaşılan şemayı arar vedefinitions.foo
alanını kullanırmyField: { $ref: 'http://url.com/sh.json#foo' }
$id: 'http://url.com/sh.json'
ile eklenen bir paylaşılan şemayı arar ve içindeki$id: '#foo'
olan objeyi arar
Basit kullanım:
fastify.addSchema({
$id: 'http://example.com/',
type: 'object',
properties: {
hello: { type: 'string' }
}
})
fastify.post('/', {
handler () {},
schema: {
body: {
type: 'array',
items: { $ref: 'http://example.com#/properties/hello' }
}
}
})
Kök referansı olarak $ref
:
fastify.addSchema({
$id: 'commonSchema',
type: 'object',
properties: {
hello: { type: 'string' }
}
})
fastify.post('/', {
handler () {},
schema: {
body: { $ref: 'commonSchema#' },
headers: { $ref: 'commonSchema#' }
}
})
Paylaşılan Şemaların Alınması
Eğer doğrulayıcı ve serileştirici özelleştirilirse, .addSchema
metodu artık kullanışlı olmayacaktır çünkü aktörler artık Fastify tarafından kontrol edilmemektedir. Fastify örneğine eklenen şemalara erişmek için, basitçe .getSchemas()
metodunu kullanabilirsiniz:
fastify.addSchema({
$id: 'schemaId',
type: 'object',
properties: {
hello: { type: 'string' }
}
})
const mySchemas = fastify.getSchemas()
const mySchema = fastify.getSchema('schemaId')
Her zamanki gibi, getSchemas
fonksiyonu kapsüllenmiş olup, seçilen kapsamda mevcut paylaşılan şemaları döndürür:
fastify.addSchema({ $id: 'one', my: 'hello' })
// sadece `one` şemasını döndürecektir
fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })
fastify.register((instance, opts, done) => {
instance.addSchema({ $id: 'two', my: 'ciao' })
// `one` ve `two` şemalarını döndürecektir
instance.get('/sub', (request, reply) => { reply.send(instance.getSchemas()) })
instance.register((subinstance, opts, done) => {
subinstance.addSchema({ $id: 'three', my: 'hola' })
// `one`, `two` ve `three` döndürecektir
subinstance.get('/deep', (request, reply) => { reply.send(subinstance.getSchemas()) })
done()
})
done()
})
Doğrulama
Rota doğrulaması, dahili olarak Ajv v8 üzerine dayanmaktadır ve bu, yüksek performanslı bir JSON Şeması doğrulayıcısıdır. Girdiyi doğrulamak oldukça kolaydır: Tek yapmanız gereken rota şemasında ihtiyacınız olan alanları eklemektir.
Desteklenen doğrulamalar:
body
: POST, PUT veya PATCH yöntemi ile gelen isteğin gövdesini doğrular.querystring
veyaquery
: sorgu dizesini doğrular.params
: rota parametrelerini doğrular.headers
: istek başlıklarını doğrular.
Tüm doğrulamalar, bir type
özelliği 'object'
olan tam bir JSON Şeması nesnesi (ve 'properties'
nesnesi içeren parametreler) veya type
ve properties
özniteliklerinin atlandığı ve parametrelerin üst seviyede listelendiği daha basit bir varyasyon olabilir (aşağıdaki örneğe bakın).
ℹ Eğer en son Ajv sürümünü (v8) kullanmanız gerekiyorsa,
schemaController
bölümünde nasıl yapılacağını okuyun.
Örnek:
const bodyJsonSchema = {
type: 'object',
required: ['requiredKey'],
properties: {
someKey: { type: 'string' },
someOtherKey: { type: 'number' },
requiredKey: {
type: 'array',
maxItems: 3,
items: { type: 'integer' }
},
nullableKey: { type: ['number', 'null'] }, // veya { type: 'number', nullable: true }
multipleTypesKey: { type: ['boolean', 'number'] },
multipleRestrictedTypesKey: {
oneOf: [
{ type: 'string', maxLength: 5 },
{ type: 'number', minimum: 10 }
]
},
enumKey: {
type: 'string',
enum: ['John', 'Foo']
},
notTypeKey: {
not: { type: 'array' }
}
}
}
const queryStringJsonSchema = {
type: 'object',
properties: {
name: { type: 'string' },
excitement: { type: 'integer' }
}
}
const paramsJsonSchema = {
type: 'object',
properties: {
par1: { type: 'string' },
par2: { type: 'number' }
}
}
const headersJsonSchema = {
type: 'object',
properties: {
'x-foo': { type: 'string' }
},
required: ['x-foo']
}
const schema = {
body: bodyJsonSchema,
querystring: queryStringJsonSchema,
params: paramsJsonSchema,
headers: headersJsonSchema
}
fastify.post('/the/url', { schema }, handler)
body
şeması için, içerik türüne göre şemayı ayırt etmek mümkündür; bunları content
özniteliği içinde iç içe yerleştirerek gerçekleştirebilirsiniz. Şema doğrulaması, istekteki Content-Type
başlığına göre uygulanır.
fastify.post('/the/url', {
schema: {
body: {
content: {
'application/json': {
schema: { type: 'object' }
},
'text/plain': {
schema: { type: 'string' }
}
// Diğer içerik türleri doğrulanmayacaktır
}
}
}
}, handler)
Ajv, şema type
anahtar kelimelerini belirtildiği gibi değerleri coerce etmeye çalışacaktır; bu, doğrulamayı geçmek ve sonraki uygun türde verilere erişmek içindir.
Fastify'da Ajv'nin varsayılan yapılandırması, querystring
içindeki dizi parametrelerini zorlamayı destekler. Örnek:
const opts = {
schema: {
querystring: {
type: 'object',
properties: {
ids: {
type: 'array',
default: []
},
},
}
}
}
fastify.get('/', opts, (request, reply) => {
reply.send({ params: request.query }) // sorgu dizisini yankılar
})
fastify.listen({ port: 3000 }, (err) => {
if (err) throw err
})
curl -X GET "http://localhost:3000/?ids=1
{"params":{"ids":["1"]}}
Her parametre türü (body, querystring, params, headers) için özel bir şema doğrulayıcı belirtebilirsiniz.
Örneğin, aşağıdaki kod yalnızca body
parametreleri için tür zorlamasını devre dışı bırakır, Ajv'nin varsayılan seçeneklerini değiştirir:
const schemaCompilers = {
body: new Ajv({
removeAdditional: false,
coerceTypes: false,
allErrors: true
}),
params: new Ajv({
removeAdditional: false,
coerceTypes: true,
allErrors: true
}),
querystring: new Ajv({
removeAdditional: false,
coerceTypes: true,
allErrors: true
}),
headers: new Ajv({
removeAdditional: false,
coerceTypes: true,
allErrors: true
})
}
server.setValidatorCompiler(req => {
if (!req.httpPart) {
throw new Error('Missing httpPart')
}
const compiler = schemaCompilers[req.httpPart]
if (!compiler) {
throw new Error(`Missing compiler for ${req.httpPart}`)
}
return compiler.compile(req.schema)
})
Daha fazla bilgi için buraya bakın.
Ajv Eklentileri
Varsayılan ajv
örneğinizle kullanmak istediğiniz bir eklentiler listesi sağlayabilirsiniz. Not edin ki eklentinin Fastify ile gönderilen Ajv sürümü ile uyumlu olması gerekir.
Eklentiler formatını kontrol etmek için
ajv options
kısmına bakın.
const fastify = require('fastify')({
ajv: {
plugins: [
require('ajv-merge-patch')
]
}
})
fastify.post('/', {
handler (req, reply) { reply.send({ ok: 1 }) },
schema: {
body: {
$patch: {
source: {
type: 'object',
properties: {
q: {
type: 'string'
}
}
},
with: [
{
op: 'add',
path: '/properties/q',
value: { type: 'number' }
}
]
}
}
}
})
fastify.post('/foo', {
handler (req, reply) { reply.send({ ok: 1 }) },
schema: {
body: {
$merge: {
source: {
type: 'object',
properties: {
q: {
type: 'string'
}
}
},
with: {
required: ['q']
}
}
}
}
})
Doğrulayıcı Derleyici
validatorCompiler
, bir işlev döndüren bir işlevdir ve body, URL parametreleri, başlıklar ve sorgu dizesini doğrular. Varsayılan validatorCompiler
, ajv doğrulama arayüzünü uygulayan bir işlev döndürür. Fastify, doğrulamayı hızlandırmak için dahili olarak bunu kullanır.
Fastify'nın temel ajv yapılandırması şu şekildedir:
{
coerceTypes: 'array', // verinin türünü, tür anahtar kelimesiyle eşleşecek şekilde değiştir
useDefaults: true, // eksik özellikleri ve öğeleri karşılık gelen varsayılan anahtardan değerler ile değiştir
removeAdditional: true, // additionalProperties false olarak ayarlandığında, ek özellikleri kaldır
uriResolver: require('fast-uri'),
addUsedSchema: false,
// AllErrors'u kesin olarak `false` olarak ayarlayın.
// `true` olarak ayarlandığında, DoS saldırısı mümkün olabilir.
allErrors: false
}
Bu temel yapılandırma, Fastify fabrikasına ajv.customOptions
vererek değiştirilebilir.
Ek yapılandırma seçeneklerini değiştirmek veya ayarlamak istiyorsanız, kendi örneğinizi oluşturmanız ve mevcut olanı geçersiz kılmanız gerekecektir:
const fastify = require('fastify')()
const Ajv = require('ajv')
const ajv = new Ajv({
removeAdditional: 'all',
useDefaults: true,
coerceTypes: 'array',
// diğer seçenekler
// ...
})
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
return ajv.compile(schema)
})
Not: Kendi doğrulayıcı örneğinizi (Ajv dahil) kullanıyorsanız, şemaları doğrulayıcıya eklemeniz gerekir; çünkü Fastify'nın varsayılan doğrulayıcı artık kullanılmıyor ve Fastify'nın addSchema
metodu hangi doğrulayıcıyı kullandığınızı bilmez.
Diğer doğrulama kütüphanelerinin kullanılması
setValidatorCompiler
işlevi, ajv
'yi neredeyse her JavaScript doğrulama kütüphanesiyle (joi, yup, ...) veya özel bir kütüphane ile değiştirmeyi kolaylaştırır:
const Joi = require('joi')
fastify.post('/the/url', {
schema: {
body: Joi.object().keys({
hello: Joi.string().required()
}).required()
},
validatorCompiler: ({ schema, method, url, httpPart }) => {
return data => schema.validate(data)
}
}, handler)
const yup = require('yup')
// Ajv’nin Fastify'da kullanılan temel seçenekleri ile uyumlu doğrulama seçenekleri
const yupOptions = {
strict: false,
abortEarly: false, // tüm hataları döndür
stripUnknown: true, // ek özellikleri kaldır
recursive: true
}
fastify.post('/the/url', {
schema: {
body: yup.object({
age: yup.number().integer().required(),
sub: yup.object().shape({
name: yup.string().required()
}).required()
})
},
validatorCompiler: ({ schema, method, url, httpPart }) => {
return function (data) {
// seçenek strict = false olduğunda, yup `validateSync` fonksiyonu
// doğrulama başarılıysa zorlanmış değeri döndürür, ya da doğrulama başarısız olursa atar
try {
const result = schema.validateSync(data, yupOptions)
return { value: result }
} catch (e) {
return { error: e }
}
}
}
}, handler)
.statusCode özelliği
Tüm doğrulama hatalarına .statusCode
özelliği eklenir ve değeri 400
olarak ayarlanır. Bu, varsayılan hata işleyicisinin yanıtın durum kodunu 400
olarak ayarlamasını garanti eder.
fastify.setErrorHandler(function (error, request, reply) {
request.log.error(error, `Bu hatanın durum kodu ${error.statusCode}`)
reply.status(error.statusCode).send(error)
})
Diğer doğrulama kütüphaneleri ile doğrulama mesajları
Fastify'nın doğrulama hata mesajları varsayılan doğrulama motoruyla sıkı bir şekilde ilişkilidir: ajv
tarafından döndürülen hatalar nihayet schemaErrorFormatter
işlevinden geçirilerek insan dostu hata mesajları oluşturulmaktadır. Ancak, schemaErrorFormatter
işlevi ajv
göz önünde bulundurularak yazılmıştır. Sonuç olarak, diğer doğrulama kütüphanelerini kullanırken garip veya eksik hata mesajlarıyla karşılaşabilirsiniz.
Bu sorunu aşmanız için, iki ana seçeneğiniz vardır:
- doğrulama işlemini (özel
schemaCompiler
'ınız tarafından döndürülen)ajv
ile aynı yapı ve biçimde hata döndürecek şekilde yazın (ancak bu, farklı doğrulama motorları arasında farklar nedeniyle zor ve karmaşık olabilir) - ya da özel doğrulama hatalarınızı kesmek ve biçimlendirmek için özel bir
errorHandler
kullanın
Özel bir errorHandler
yazmanıza yardımcı olmak için, Fastify tüm doğrulama hatalarına iki özellik ekler:
validation
: doğrulama işlevinin döndürdüğü nesneninerror
özelliğinin içeriği (özelschemaCompiler
'ınızdan döndürülür)validationContext
: doğrulama hatasının meydana geldiği 'bağlam' (body, params, query, headers)
Aşağıda, doğrulama hatalarını işleyen bir özel errorHandler
ın çok basit bir örneği gösterilmektedir:
const errorHandler = (error, request, reply) => {
const statusCode = error.statusCode
let response
const { validation, validationContext } = error
// doğrulama hatası olup olmadığını kontrol et
if (validation) {
response = {
// validationContext 'body' veya 'params' veya 'headers' veya 'query' olacaktır
message: `Doğrulama hatası ${validationContext} doğrularken oluştu...`,
// bu, doğrulama kütüphanenizin sonucu...
errors: validation
}
} else {
response = {
message: 'Bir hata oluştu...'
}
}
// burada ek çalışma, örn. hatayı kaydetme
// ...
reply.status(statusCode).send(response)
}
Serileştirme
Genellikle, verilerinizi istemcilere JSON olarak gönderirsiniz ve Fastify, bir yanıt şeması sağladığınızda kullanılan güçlü bir araca sahiptir, fast-json-stringify. Bir çıktı şeması kullanmanızı teşvik ederiz, çünkü bu, verimliliği önemli ölçüde artırabilir ve hassas bilgilerin yanlışlıkla ifşasını engellemeye yardımcı olabilir.
Örnek:
const schema = {
response: {
200: {
type: 'object',
properties: {
value: { type: 'string' },
otherValue: { type: 'boolean' }
}
}
}
}
fastify.post('/the/url', { schema }, handler)
Gördüğünüz gibi, yanıt şeması durum koduna dayanır. Aynı şemayı birden fazla durum kodu için kullanmak istiyorsanız, '2xx'
veya default
gibi kullanabilirsiniz, örneğin:
const schema = {
response: {
default: {
type: 'object',
properties: {
error: {
type: 'boolean',
default: true
}
}
},
'2xx': {
type: 'object',
properties: {
value: { type: 'string' },
otherValue: { type: 'boolean' }
}
},
201: {
// sözleşme sözdizimi
value: { type: 'string' }
}
}
}
fastify.post('/the/url', { schema }, handler)
Farklı içerik türleri için belirli bir yanıt şemasına da sahip olabilirsiniz. Örneğin:
const schema = {
response: {
200: {
description: 'Farklı içerik türlerini destekleyen yanıt şeması',
content: {
'application/json': {
schema: {
name: { type: 'string' },
image: { type: 'string' },
address: { type: 'string' }
}
},
'application/vnd.v1+json': {
schema: {
type: 'array',
items: { $ref: 'test' }
}
}
}
},
'3xx': {
content: {
'application/vnd.v2+json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'string' }
}
}
}
},
default: {
content: {
// */* tüm içerik türlerini karşılıyor
'*/*': {
schema: {
desc: { type: 'string' }
}
}
}
}
}
}
fastify.post('/url', { schema }, handler)
Serileştirici Derleyici
serializerCompiler
, bir işlev döndüren bir işlevdir ve bir girdi nesnesinden bir dize döndürmelidir. Bir yanıt JSON Şeması tanımladığınızda, her rota için varsayılan serileştirme yöntemini değiştirmek için bir işlev sağlayarak bunu gerçekleştirebilirsiniz.
fastify.setSerializerCompiler(({ schema, method, url, httpStatus, contentType }) => {
return data => JSON.stringify(data)
})
fastify.get('/user', {
handler (req, reply) {
reply.send({ id: 1, name: 'Foo', image: 'BÜYÜK GÖRÜNTÜ' })
},
schema: {
response: {
'2xx': {
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' }
}
}
}
}
})
Belirli bir kodunuzun çok özel bir parçasında bir özel serileştiriciye ihtiyacınız varsa, bunu reply.serializer(...)
ile ayarlayabilirsiniz.