working version with pages router

This commit is contained in:
goenning 2023-09-01 11:14:44 +01:00
parent f4edab87af
commit c3b93e592a
13 changed files with 193 additions and 3 deletions

View file

@ -5,7 +5,7 @@ export default async function Home() {
await trackEvent('page_view', { page: 'home' }); await trackEvent('page_view', { page: 'home' });
return ( return (
<main> <main>
Hello from Next.js App Router <br /> <Counter /> Aptabase + Next.js App Router <br /> <Counter />
</main> </main>
); );
} }

View file

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View file

@ -0,0 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
experimental: {
instrumentationHook: true,
},
};
module.exports = nextConfig;

View file

@ -0,0 +1,21 @@
{
"name": "next-with-pages",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 4000",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@types/node": "20.5.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"next": "13.4.19",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.2.2",
"@aptabase/nextjs": "*"
}
}

View file

@ -0,0 +1,25 @@
import { useAptabase } from '@aptabase/nextjs';
import { useState } from 'react';
export function Counter() {
const { trackEvent } = useAptabase();
const [count, setCount] = useState(0);
function increment() {
setCount((c) => c + 1);
trackEvent('increment');
}
function decrement() {
setCount((c) => c - 1);
trackEvent('decrement');
}
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}

View file

@ -0,0 +1,5 @@
import { init } from '@aptabase/nextjs';
export function register() {
init('A-DEV-0000000000');
}

View file

@ -0,0 +1,10 @@
import { AptabaseProvider } from '@aptabase/nextjs';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<AptabaseProvider appKey="A-DEV-0000000000">
<Component {...pageProps} />
</AptabaseProvider>
);
}

View file

@ -0,0 +1,13 @@
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

View file

@ -0,0 +1,12 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import { trackEvent } from '@aptabase/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
type Data = {
name: string;
};
export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
await trackEvent('hello', { name: 'John Doe' }, req);
res.status(200).json({ name: 'John Doe' });
}

View file

@ -0,0 +1,25 @@
import { Counter } from '@/components/Counter';
import { trackEvent } from '@aptabase/nextjs/server';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
await trackEvent('page_view', { page: 'home' }, req);
return { props: {} };
};
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<main>
Aptabase + Next.js Pages Router <br />
<Counter />
</main>
</>
);
}

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

17
package-lock.json generated
View file

@ -33,6 +33,19 @@
"typescript": "5.2.2" "typescript": "5.2.2"
} }
}, },
"examples/next-with-pages": {
"version": "0.1.0",
"dependencies": {
"@aptabase/nextjs": "*",
"@types/node": "20.5.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"next": "13.4.19",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.2.2"
}
},
"node_modules/@aptabase/nextjs": { "node_modules/@aptabase/nextjs": {
"resolved": "packages/nextjs", "resolved": "packages/nextjs",
"link": true "link": true
@ -590,6 +603,10 @@
"resolved": "examples/next-with-approuter", "resolved": "examples/next-with-approuter",
"link": true "link": true
}, },
"node_modules/next-with-pages": {
"resolved": "examples/next-with-pages",
"link": true
},
"node_modules/path-parse": { "node_modules/path-parse": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",

View file

@ -1,9 +1,17 @@
import { type NextIncomingMessage } from 'next/dist/server/request-meta';
import { headers } from 'next/headers'; import { headers } from 'next/headers';
export async function trackEvent(eventName: string, props?: Record<string, string | number | boolean>): Promise<void> { export async function trackEvent(
eventName: string,
props?: Record<string, string | number | boolean>,
req?: NextIncomingMessage,
): Promise<void> {
const appKey = globalThis.__APTABASE__.appKey; const appKey = globalThis.__APTABASE__.appKey;
if (!appKey) return Promise.resolve(); if (!appKey) return Promise.resolve();
const userAgent = getUserAgent(req);
if (!userAgent) return Promise.resolve();
const body = JSON.stringify({ const body = JSON.stringify({
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
sessionId: 'CHANGE-THIS', sessionId: 'CHANGE-THIS',
@ -21,7 +29,7 @@ export async function trackEvent(eventName: string, props?: Record<string, strin
const response = await fetch('http://localhost:3000/api/v0/event', { const response = await fetch('http://localhost:3000/api/v0/event', {
method: 'POST', method: 'POST',
headers: { headers: {
'User-Agent': headers().get('User-Agent') ?? '', 'User-Agent': userAgent,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'App-Key': appKey, 'App-Key': appKey,
}, },
@ -37,3 +45,21 @@ export async function trackEvent(eventName: string, props?: Record<string, strin
console.warn(`Failed to send event "${eventName}": ${e}`); console.warn(`Failed to send event "${eventName}": ${e}`);
} }
} }
function getUserAgent(req?: NextIncomingMessage): string | undefined {
if (req) {
return req.headers['user-agent'] ?? 'Unknown';
}
// headers() might throw an error if called outside of a request context.
try {
return headers().get('User-Agent') ?? 'Unknown';
} catch {}
// If we're here, we're probably using the Pages Router and the user forgot to pass the req parameter.
if (!req) {
console.warn("Aptabase: The 'req' parameter of trackEvent is required when using Pages Router.");
}
return undefined;
}