Astro js server-side rendering custom error page - astrojs

I've seen the custom 404.astro page. But how does this translate to server-side rendering? (Using the node.js adapter if that matters.)
Looking at the Response docs, I thought about writing something like:
---
import { render } from 'astro/???'
import { getProduct } from '../api'
import { MyErrorAstroComponent } from '../MyErrorAstroComponent'
const product = await getProduct(Astro.params.id)
if (!product) {
const props = { status: 404 }
return new Response(render(MyErrorAstroComponent, props), {
status: 404
})
}
---
<html>
<!-- Success page here... -->
</html>
Same question for a 500 or 503 page, e.g. if the database times out or something...
I've found How can I render a astro component to a HTML string?, but no good answer...

Introduction
404 can be handled at the bottom of the middleware pipeline when none of them could handle the request.
here more details on how to implement it in an express js project
https://expressjs.com/en/starter/faq.html#:~:text=How%20do%20I%20handle%20404%20responses%3F
Astro middleware
here working fine, implemented in an Astro as node middleware with node project.
const app = express();
app.use(express.static('dist/client/'))
app.use(express.static('dist/client/raw'))
app.use(authRouter)
app.use(ssrHandler);
app.use((req, res, next) => {
res.status(404).send("Sorry can't find that!")
})
here a link to a full working project
https://github.com/MicroWebStacks/astro-big-doc/blob/a2049e286424c256e9d406f7df84dfedf65527ad/server/server.js#L17
Astro Standalone
in standalone mode it is possible to add a collector page such as [...any].astro that collects all requests not hit by other pages and then pick the required content to be returned and place it in its body as follows
---
import Page_404 from './404.astro'
const {any} = Astro.params;
console.log(`${any} does not exist`)
---
<Page_404/>
Examples
here are examples where this can be tested
Github repo : https://github.com/MicroWebStacks/astro-examples#08_404-error-handling
StackBliz : https://stackblitz.com/github/MicroWebStacks/astro-examples/tree/main/08_404-error-handling
CodeSandbox : https://codesandbox.io/s/github/MicroWebStacks/astro-examples/tree/main/08_404-error-handling
Gitpod (test in prod) : https://gitpod.io/?on=gitpod#https://github.com/MicroWebStacks/astro-examples/tree/main/08_404-error-handling

Related

fetch weatherapi.com with async await react hooks

Can anyone help me why my code doesn't work to fetch API?
I have to build a weather app from several components, must build it structured.
My plan is to have one service component that I have API service in there. then I have to make 3 more components, search component to handle the city search, weatherToday component to show today weather, and weatherForecast component to show five days forecast.
And I have to fetch the API with async an await. Here is the code that I tried(just now I have the code in my App.js just to try if my fetch work)
import { useState, useEffect } from "react";
function App() {
const [data, setData] = useState();
const fetchData = async () => {
await fetch(
`http://api.weatherapi.com/v1/forecast.json?key=1d172d3904e246849d3183628230802&q=Stockholm&days=6&aqi=no&alerts=no`
)
.then((response) => {
return response.json();
})
.then((data) => {
setData(data);
});
};
useEffect(() => {
fetchData();
}, []);
return (
<>
<h3>{data.location.name}</h3>
<p>{data.current.temp_c}</p>
<p>{data.location.localtime}</p>
</>
);
}
export default App;
First of all, welcome to React and StackOverflow!
There's a few issues here:
The main issue is CORS. You can't call this API from your browser - it's meant to be called from a server (backend). I highly recommend using Next.js since you like React, it uses that as it's framework - but it allows you to have Server Components, essentially an Express backend, so that you can perform this API call - then retrieve that data using this client component just to display the data (not to fetch it).
Another issue (but not the problem here) is reusing the data variable in the local scope of then((data) => is not good when you have data defined higher up in the component scope for your state. Use then((d) => instead.
I created a Next.js 13 sandbox for you with this working API call to get you started:
https://codesandbox.io/p/sandbox/broken-field-4nsp1p
In Next.js 13, you can use the app folder, where every component is a Server Component by default. Then you can create Client Component, like the one you have above - you simply have to add use client to the very top of the file, that's it.
Since Next.js 13 is very new (the app folder and concept of Server Components very new and bound to change), you would want to potentially just stick with the pages folder.
In there, you'll see the client component which calls the api folder's getWeather API call.
Finally, you shared your private key with the public. You need to destroy and regenerate that key now:
From the WeatherAPI.com Docs:
Authentication
API access to the data is protected by an API key. If at anytime, you
find the API key has become vulnerable, please regenerate the key
using Regenerate button next to the API key.
https://www.weatherapi.com/docs/
If don't want to use Next.js - then you'll need to use some sort of backend, like Firebase Functions or Google Cloud Functions, etc. Next.js is probably the easiest thing to adapt if you like React though!
Learn about Next.js 13 & /app folder:
https://beta.nextjs.org/docs/getting-started
Learn about Next.js 12 & /pages folder:
https://nextjs.org/docs
Implementing Fetch via Next.js 13
app/head.tsx
export default function Head() {
return (
<>
<title>Weather App</title>
<meta content="width=device-width, initial-scale=1" name="viewport" />
</>
);
}
app.layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>{children}</body>
</html>
);
}
app/page.tsx
const App = async () => {
console.log("App.js");
const results = await fetch(
`http://api.weatherapi.com/v1/forecast.json?key=1d172d3904e246849d3183628230802&q=Stockholm&days=6&aqi=no&alerts=no`
);
const json = await results.json();
console.log("json", json);
return (
<>
<h3>{json.location.name}</h3>
<p>{json.location.temp_c}</p>
<p>{json.location.localtime}</p>
</>
);
};
export default App;
When visiting either / or /weather, you will see the same results, since the example is implemented in both v12 and v13:
--- OR --- Implementing Fetch via Next.js 12
pages/weather.tsx
async function getData() {
const res = await fetch("/api/getWeather");
console.log("res", res);
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
const json = await res.json();
console.log({ json });
return json;
}
export default async function Page() {
const data = await getData();
console.log("data", data);
return (
<main>
<h3>{data.location.name}</h3>
<p>{data.current.temp_c}</p>
<p>{data.location.localtime}</p>
</main>
);
}
pages/api/getWeather.ts
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const results = await fetch(
`http://api.weatherapi.com/v1/forecast.json?key=1d172d3904e246849d3183628230802&q=Stockholm&days=6&aqi=no&alerts=no`
);
const json = await results.json();
console.log("json", json);
res.status(200).send(json);
}
Remember to revoke your exposed secret API key.
I hope this helps you start building your app. Good luck!

How to cache a Server Side Rendered Page using Dynamic Routes with Next.js

I'm deploying a Next.js Application on AWS using ECS/Fargate (can't use Amplify due to custom logging and monitoring required). My Application is using api-routes and dynamic-routes with Server-Side-Rendering(getServerSideProps).
Lets say for the SSRed page the URL is something like: domain.com/foopage/[id]?powerlevel=10000.
The Code Looks something like:
// pages/foopage/[id].tsx
import React from "react";
import type { GetServerSideProps } from "next";
import Head from "next/head";
export default function FooPage({ id }: Record<string, string>) {
return (
<div>
<Head>
<title>Title</title>
</Head>
<main>
<h1>
Say Hi to {id}!
</h1>
</main>
</div>
);
}
export const getServerSideProps: GetServerSideProps = async (context) => {
context.res.setHeader(
"Cache-Control",
"public, s-maxage=10, stale-while-revalidate=59"
);
const { id } = context.query;
const response = await fetch(
`${process.env.CMS_URL}/api/configurations?id=${id}`
);
const jsonResponse = await response.json();
return {
props: { id },
};
};
I want to cache these pages so that the server doesn't have to generate the page every time.
I know that if you are using Vercel, Edge Caching works without configuration. But I'm bound to use AWS due to business-requirements. So how exactly to do it in AWS? Is it possible to do using Lambda#Edge and if so what could be the possible lambda function?
I know that if you are using Vercel, Edge Caching works without
configuration. But I'm bound to use AWS due to business-requirements.
So how exactly to do it in AWS?
If you want edge caching, you need to use a CDN. Amazon's CDN is CloudFront.

nuxtjs middleware rest API raw data requests

I have build nuxtjs example with built in rest API requests with middleware technique. In index.js of my middleware section I put some code :
export default {
handler(req, res) {
res.write('Everything ok!')
console.log(req);
console.log(res);
res.end()
},
path: '/test15'
}
When I call http://ip:port/test15/?cmd=somecmd&param=testparam
In console.log I get params data in log, everything nice. No matter which method used, post or get, it also fixed in log.
The problem is when I try to send raw data (ex json) in request body or form data. I can`t see them in any log output.
So question is, is it possible to send some data in such requests via middleware ?
Thanks!
middleware in nuxt is a sandwich for internal routes aka client side. For your question serverMiddleware is the answer that work on the server side. You can checkout more here
Quick example:
In your nuxt.config.js file add like below
serverMiddleware: [
{ path: '/api/subscribe', handler: '~/api/subscribe' }
],
Then create an api folder you can create subscribe.js file to add relative api route.
import express from 'express'
const app = express()
app.get('/subscribe', async (req, res) => {
res.send('love to the world');
})
export default {
path: '/api',
handler: app
}

Making credentialed requests with Bokeh AjaxDataSource

I have a plot set up to use an AjaxDataSource. This is working pretty well in my local development, and was working as deployed in my Kubernetes cluster. However, after I added HTTPS and Google IAP (Identity-Aware Proxy) to my plotting app, all of the requests to the data-url for my AjaxDataSource are rejected by the Google IAP service.
I have run into this issue in the past with other AJAX requests to Google IAP-protected services, and resolved it by setting {withCredentials: true} in my axios requests. However, I do not have this option while working with Bokeh's AjaxDataSource. How do I get BokehJS to pass the cookies to my service in the AjaxDataSource?
AjaxDataSource can pass headers:
ajax_source.headers = { 'x-my-custom-header': 'some value' }
There's not any way to set cookies (that would be set on the viewer's browser... which does not seem relevant in this context). Doing that would require building a custom extension.
Thanks to bigreddot for pointing me in the right direction. I was able to build a custom extension that did what I needed. Here's the source code for that extension:
from bokeh.models import AjaxDataSource
from bokeh.util.compiler import TypeScript
TS_CODE = """
import {AjaxDataSource} from "models/sources";
export class CredentialedAjaxDataSource extends AjaxDataSource {
prepare_request(): XMLHttpRequest {
const xhr = new XMLHttpRequest();
xhr.open(this.method, this.data_url, true);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", this.content_type);
const http_headers = this.http_headers;
for (const name in http_headers) {
const value = http_headers[name];
xhr.setRequestHeader(name, value)
}
return xhr;
}
}
"""
class CredentialedAjaxDataSource(AjaxDataSource):
__implementation__ = TypeScript(TS_CODE)
Bokeh extensions documentation: https://docs.bokeh.org/en/latest/docs/user_guide/extensions.html

Not able to make the ajax request with my header given by ember-simple-auth authorizers

I have developed a template (feedheader.hbs) which I am rendering in my feed template like {{ render 'feedheader' feedheader}}.This feedheader contains the username and profile pic of user.I need to send the the requests to http://example.com/api/users/profile/?targetuser=-1 along with my auth header to get the user details.So I made a controller for feedheader and made the ajax requests.I am using ember-simple-auth (fb login ) for authorization.
controller.js
import Ember from 'ember';
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';
const { service } = Ember.inject;
export default Ember.Controller.extend(OAuth2Bearer,{
session:service('session'),
init:function(){
this.get('session').authorize('authorizer:application', (headerName, headerValue) => {
const headers = {};
headers[headerName] = headerValue;
Ember.$.ajax('http://example.com/api/users/profile/?targetuser=-1', { headers });
});
}
});
But it throws an error in console
"Uncaught Error: Assertion Failed: Expected hash or Mixin instance, got [object Function]"
authorizers(application.js)
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';
export default OAuth2Bearer.extend();
The authorizers is working fine in my adapter.There is some simple thing I am missing and not able to fix this.Please tell how to send the ajax request with my authorizers header.
The way you're sending the headers doesn't look right to me. Instead of
Ember.$.ajax('http://example.com/api/users/profile/?targetuser=-1', { headers });
it should be
Ember.$.ajax('http://example.com/api/users/profile/?targetuser=-1', { headers: headers });

Resources