feat: upper case the name of the endpoints (#7783)

Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com>
Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
This commit is contained in:
Emanuele Stoppa 2023-07-26 08:53:31 +01:00
parent e9f697cf14
commit a6e5135765
45 changed files with 116 additions and 53 deletions

View file

@ -0,0 +1,35 @@
---
'astro': major
---
Lowercase names for endpoint functions are now deprecated.
Rename functions to their uppercase equivalent:
```diff
- export function get() {
+ export function GET() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
- export function post() {
+ export function POST() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
- export function put() {
+ export function PUT() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
- export function all() {
+ export function ALL() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
// you can use the whole word "DELETE"
- export function del() {
+ export function DELETE() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
```

View file

@ -24,7 +24,7 @@ async function loadRemoteImage(src: URL) {
/** /**
* Endpoint used in dev and SSR to serve optimized images by the base image services * Endpoint used in dev and SSR to serve optimized images by the base image services
*/ */
export const get: APIRoute = async ({ request }) => { export const GET: APIRoute = async ({ request }) => {
try { try {
const imageService = await getConfiguredImageService(); const imageService = await getConfiguredImageService();

View file

@ -7,13 +7,13 @@ import type {
Params, Params,
} from '../../@types/astro'; } from '../../@types/astro';
import type { Environment, RenderContext } from '../render/index'; import type { Environment, RenderContext } from '../render/index';
import { renderEndpoint } from '../../runtime/server/index.js'; import { renderEndpoint } from '../../runtime/server/index.js';
import { ASTRO_VERSION } from '../constants.js'; import { ASTRO_VERSION } from '../constants.js';
import { AstroCookies, attachToResponse } from '../cookies/index.js'; import { AstroCookies, attachToResponse } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js';
import { warn } from '../logger/core.js'; import { warn } from '../logger/core.js';
import { callMiddleware } from '../middleware/callMiddleware.js'; import { callMiddleware } from '../middleware/callMiddleware.js';
const clientAddressSymbol = Symbol.for('astro.clientAddress'); const clientAddressSymbol = Symbol.for('astro.clientAddress');
const clientLocalsSymbol = Symbol.for('astro.locals'); const clientLocalsSymbol = Symbol.for('astro.locals');
@ -119,11 +119,11 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
onRequest as MiddlewareEndpointHandler, onRequest as MiddlewareEndpointHandler,
context, context,
async () => { async () => {
return await renderEndpoint(mod, context, env.ssr); return await renderEndpoint(mod, context, env.ssr, env.logging);
} }
); );
} else { } else {
response = await renderEndpoint(mod, context, env.ssr); response = await renderEndpoint(mod, context, env.ssr, env.logging);
} }
if (response instanceof Response) { if (response instanceof Response) {

View file

@ -1,28 +1,56 @@
import type { APIContext, EndpointHandler, Params } from '../../@types/astro'; import type { APIContext, EndpointHandler, Params } from '../../@types/astro';
import { type LogOptions, warn } from '../../core/logger/core.js';
function getHandlerFromModule(mod: EndpointHandler, method: string) { function getHandlerFromModule(mod: EndpointHandler, method: string, logging: LogOptions) {
const lowerCaseMethod = method.toLowerCase();
// TODO: remove in Astro 4.0
if (mod[lowerCaseMethod]) {
warn(
logging,
'astro',
`Lower case endpoint names are deprecated and will not be supported in Astro 4.0. Rename the endpoint ${lowerCaseMethod} to ${method}.`
);
}
// If there was an exact match on `method`, return that function. // If there was an exact match on `method`, return that function.
if (mod[method]) { if (mod[method]) {
return mod[method]; return mod[method];
} }
// TODO: remove in Astro 4.0
if (mod[lowerCaseMethod]) {
return mod[lowerCaseMethod];
}
// TODO: remove in Astro 4.0
// Handle `del` instead of `delete`, since `delete` is a reserved word in JS. // Handle `del` instead of `delete`, since `delete` is a reserved word in JS.
if (method === 'delete' && mod['del']) { if (method === 'delete' && mod['del']) {
return mod['del']; return mod['del'];
} }
// TODO: remove in Astro 4.0
// If a single `all` handler was used, return that function. // If a single `all` handler was used, return that function.
if (mod['all']) { if (mod['all']) {
return mod['all']; return mod['all'];
} }
if (mod['ALL']) {
return mod['ALL'];
}
// Otherwise, no handler found. // Otherwise, no handler found.
return undefined; return undefined;
} }
/** Renders an endpoint request to completion, returning the body. */ /** Renders an endpoint request to completion, returning the body. */
export async function renderEndpoint(mod: EndpointHandler, context: APIContext, ssr: boolean) { export async function renderEndpoint(
mod: EndpointHandler,
context: APIContext,
ssr: boolean,
logging: LogOptions
) {
const { request, params } = context; const { request, params } = context;
const chosenMethod = request.method?.toLowerCase();
const handler = getHandlerFromModule(mod, chosenMethod); const chosenMethod = request.method?.toUpperCase();
if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'get') { const handler = getHandlerFromModule(mod, chosenMethod, logging);
// TODO: remove the 'get' check in Astro 4.0
if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET' && chosenMethod !== 'get') {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn(` console.warn(`
${chosenMethod} requests are not available when building a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` with an \`export const prerender = false\` to handle ${chosenMethod} requests.`); ${chosenMethod} requests are not available when building a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` with an \`export const prerender = false\` to handle ${chosenMethod} requests.`);

View file

@ -1,5 +1,5 @@
import type { APIRoute } from 'astro'; import type { APIRoute } from 'astro';
export const get: APIRoute = async function () { export const GET: APIRoute = async function () {
return new Response(new Uint8Array([0xff])); return new Response(new Uint8Array([0xff]));
}; };

View file

@ -14,7 +14,7 @@ export function getStaticPaths() {
] ]
} }
export function get({ params, request }) { export function GET({ params, request }) {
return { return {
body: JSON.stringify({ body: JSON.stringify({
param: params.param, param: params.param,

View file

@ -1,5 +1,5 @@
export function post({ cookies }) { export function POST({ cookies }) {
const mode = cookies.get('prefs').json().mode; const mode = cookies.get('prefs').json().mode;
cookies.set('prefs', { cookies.set('prefs', {

View file

@ -5,7 +5,7 @@ export async function getStaticPaths() {
]; ];
} }
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
title: '[slug]' title: '[slug]'

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
const docs = await import.meta.glob('./*.md', { eager: true }); const docs = await import.meta.glob('./*.md', { eager: true });
return { return {
body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)), body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)),

View file

@ -1,6 +1,6 @@
import { getHeadings } from './with-layout.md'; import { getHeadings } from './with-layout.md';
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
headings: getHeadings(), headings: getHeadings(),

View file

@ -1,6 +1,6 @@
import { rawContent, compiledContent } from './basic.md'; import { rawContent, compiledContent } from './basic.md';
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
raw: rawContent(), raw: rawContent(),

View file

@ -1,6 +1,6 @@
import { frontmatter } from './vite-env-vars.md'; import { frontmatter } from './vite-env-vars.md';
export async function get() { export async function GET() {
return { return {
body: JSON.stringify(frontmatter), body: JSON.stringify(frontmatter),
} }

View file

@ -1,6 +1,6 @@
import { getEntry, getEntries } from 'astro:content'; import { getEntry, getEntries } from 'astro:content';
export async function get() { export async function GET() {
const welcomePost = await getEntry('blog', 'welcome'); const welcomePost = await getEntry('blog', 'welcome');
if (!welcomePost?.data) { if (!welcomePost?.data) {

View file

@ -2,7 +2,7 @@ import { getCollection } from 'astro:content';
import * as devalue from 'devalue'; import * as devalue from 'devalue';
import { stripAllRenderFn } from '../utils.js'; import { stripAllRenderFn } from '../utils.js';
export async function get() { export async function GET() {
const withoutConfig = stripAllRenderFn(await getCollection('without-config')); const withoutConfig = stripAllRenderFn(await getCollection('without-config'));
const withSchemaConfig = stripAllRenderFn(await getCollection('with-schema-config')); const withSchemaConfig = stripAllRenderFn(await getCollection('with-schema-config'));
const withSlugConfig = stripAllRenderFn(await getCollection('with-custom-slugs')); const withSlugConfig = stripAllRenderFn(await getCollection('with-custom-slugs'));

View file

@ -2,7 +2,7 @@ import { getEntryBySlug } from 'astro:content';
import * as devalue from 'devalue'; import * as devalue from 'devalue';
import { stripRenderFn } from '../utils.js'; import { stripRenderFn } from '../utils.js';
export async function get() { export async function GET() {
const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia')); const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia'));
const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one')); const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one'));
const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-custom-slugs', 'interesting-two')); const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-custom-slugs', 'interesting-two'));

View file

@ -1,6 +1,6 @@
import type { APIRoute } from "../../../../../src/@types/astro"; import type { APIRoute } from "../../../../../src/@types/astro";
export const get = (async ({ params, request }) => { export const GET = (async ({ params, request }) => {
const url = new URL(request.url); const url = new URL(request.url);
const src = url.searchParams.get("src"); const src = url.searchParams.get("src");

View file

@ -7,7 +7,7 @@ export function getStaticPaths() {
} }
/** @param {import('astro').APIContext} params */ /** @param {import('astro').APIContext} params */
export async function get({ params }) { export async function GET({ params }) {
const { id } = params; const { id } = params;
const author = await getEntry('authors-without-config', id); const author = await getEntry('authors-without-config', id);
if (!author) { if (!author) {

View file

@ -1,6 +1,6 @@
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
export async function get() { export async function GET() {
const authors = await getCollection('authors-without-config'); const authors = await getCollection('authors-without-config');
return { return {

View file

@ -7,7 +7,7 @@ export function getStaticPaths() {
} }
/** @param {import('astro').APIContext} params */ /** @param {import('astro').APIContext} params */
export async function get({ params }) { export async function GET({ params }) {
const { lang } = params; const { lang } = params;
const translations = await getEntry('i18n', lang); const translations = await getEntry('i18n', lang);
if (!translations) { if (!translations) {

View file

@ -1,6 +1,6 @@
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
export async function get() { export async function GET() {
const translations = await getCollection('i18n'); const translations = await getCollection('i18n');
return { return {

View file

@ -2,7 +2,7 @@ import type { APIRoute } from "astro";
const slugs = ["one", undefined]; const slugs = ["one", undefined];
export const get: APIRoute = ({ params }) => { export const GET: APIRoute = ({ params }) => {
return { return {
body: JSON.stringify({ body: JSON.stringify({
slug: params.slug || "index", slug: params.slug || "index",

View file

@ -1,4 +1,4 @@
export function get() { export function GET() {
const object = { const object = {
name: 'Endpoint!!', name: 'Endpoint!!',
}; };

View file

@ -1,7 +1,7 @@
// Returns the file body for this non-HTML file. // Returns the file body for this non-HTML file.
// The content type is based off of the extension in the filename, // The content type is based off of the extension in the filename,
// in this case: about.json. // in this case: about.json.
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
name: 'Astro', name: 'Astro',

View file

@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs';
import type { APIRoute } from 'astro'; import type { APIRoute } from 'astro';
export const get: APIRoute = async function get() { export const GET: APIRoute = async function get() {
try { try {
// Image is in the public domain. Sourced from // Image is in the public domain. Sourced from
// https://en.wikipedia.org/wiki/File:Portrait_placeholder.png // https://en.wikipedia.org/wiki/File:Portrait_placeholder.png

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro'; import type { APIRoute } from 'astro';
export const get: APIRoute = async ({ params }) => { export const GET: APIRoute = async ({ params }) => {
return { return {
body: JSON.stringify({ body: JSON.stringify({
path: params.slug, path: params.slug,

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro'; import type { APIRoute } from 'astro';
export const get: APIRoute = async ({ params }) => { export const GET: APIRoute = async ({ params }) => {
return { return {
body: JSON.stringify({ body: JSON.stringify({
foo: params.foo, foo: params.foo,

View file

@ -1,5 +1,5 @@
export function post() { export function POST() {
return { return {
body: JSON.stringify({ ok: true }) body: JSON.stringify({ ok: true })
}; };

View file

@ -1,6 +1,6 @@
import fs from 'node:fs'; import fs from 'node:fs';
export function get() { export function GET() {
return { return {
body: 'ok' body: 'ok'
}; };

View file

@ -1,7 +1,7 @@
/** /**
* @param {import('astro').APIContext} api * @param {import('astro').APIContext} api
*/ */
export function get(ctx) { export function GET(ctx) {
return { return {
body: JSON.stringify({ body: JSON.stringify({
cookiesExist: !!ctx.cookies, cookiesExist: !!ctx.cookies,

View file

@ -1,5 +1,5 @@
export function get() { export function GET() {
return { return {
body: JSON.stringify([ body: JSON.stringify([
{ name: 'lettuce' }, { name: 'lettuce' },
@ -9,7 +9,7 @@ export function get() {
}; };
} }
export async function post({ params, request }) { export async function POST({ params, request }) {
const body = await request.text(); const body = await request.text();
return new Response(body === `some data` ? `ok` : `not ok`, { return new Response(body === `some data` ? `ok` : `not ok`, {
status: 200, status: 200,

View file

@ -1,5 +1,5 @@
/** @type {import('astro').APIRoute} */ /** @type {import('astro').APIRoute} */
export function post({ cookies }) { export function POST({ cookies }) {
cookies.set('foo', 'foo', { cookies.set('foo', 'foo', {
httpOnly: true httpOnly: true
}); });

View file

@ -1,5 +1,5 @@
export function get({ params }) { export function GET({ params }) {
return { return {
body: JSON.stringify(params) body: JSON.stringify(params)
}; };

View file

@ -1,5 +1,5 @@
export async function get({ locals }) { export async function GET({ locals }) {
let out = { ...locals }; let out = { ...locals };
return new Response(JSON.stringify(out), { return new Response(JSON.stringify(out), {

View file

@ -7,7 +7,7 @@ export async function getStaticPaths() {
]; ];
} }
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
title: '[slug]' title: '[slug]'

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
name: 'Astro Technology Company', name: 'Astro Technology Company',

View file

@ -5,7 +5,7 @@ export async function getStaticPaths() {
] ]
} }
export async function get({ params }) { export async function GET({ params }) {
return { return {
body: JSON.stringify({ body: JSON.stringify({
slug: params.slug, slug: params.slug,

View file

@ -13,7 +13,7 @@ async function fetchPosts() {
return posts.sort((a, b) => a.title.localeCompare(b.title)); return posts.sort((a, b) => a.title.localeCompare(b.title));
} }
export async function get() { export async function GET() {
const posts = await fetchPosts(); const posts = await fetchPosts();
return { return {

View file

@ -5,7 +5,7 @@ export async function getStaticPaths() {
]; ];
} }
export async function get({ params }) { export async function GET({ params }) {
return { return {
body: JSON.stringify({ body: JSON.stringify({
slug: params.slug, slug: params.slug,

View file

@ -5,7 +5,7 @@ export async function getStaticPaths() {
]; ];
} }
export async function get({ params }) { export async function GET({ params }) {
return { return {
body: JSON.stringify({ body: JSON.stringify({
slug: params.slug, slug: params.slug,

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
return { return {
body: JSON.stringify({ body: JSON.stringify({
title: 'home' title: 'home'

View file

@ -5,7 +5,7 @@ export async function getStaticPaths() {
]; ];
} }
export async function get({ params }) { export async function GET({ params }) {
return { return {
body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200"> body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
<title>${params.image}</title> <title>${params.image}</title>

View file

@ -1,6 +1,6 @@
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
export async function get({ params, request }) { export async function GET({ params, request }) {
const buffer = readFileSync(new URL('../../astro.png', import.meta.url)); const buffer = readFileSync(new URL('../../astro.png', import.meta.url));
return { return {
body: buffer.toString('hex'), body: buffer.toString('hex'),

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
return { return {
body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200"> body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
<title>Static SVG</title> <title>Static SVG</title>

View file

@ -20,7 +20,7 @@ async function loadRemoteImage(src: URL) {
} }
} }
export const get: APIRoute = async ({ request }) => { export const GET: APIRoute = async ({ request }) => {
try { try {
const url = new URL(request.url); const url = new URL(request.url);
const transform = loader.parseTransform(url.searchParams); const transform = loader.parseTransform(url.searchParams);

View file

@ -1,4 +1,4 @@
import { Tokenizer } from '@markdoc/markdoc'; import type { Tokenizer } from '@markdoc/markdoc';
import { Parser } from 'htmlparser2'; import { Parser } from 'htmlparser2';
import type * as Token from 'markdown-it/lib/token'; import type * as Token from 'markdown-it/lib/token';