diff --git a/examples/next-with-approuter/src/app/page.tsx b/examples/next-with-approuter/src/app/page.tsx
index 86d95df..40e0059 100644
--- a/examples/next-with-approuter/src/app/page.tsx
+++ b/examples/next-with-approuter/src/app/page.tsx
@@ -5,7 +5,7 @@ export default async function Home() {
await trackEvent('page_view', { page: 'home' });
return (
- Hello from Next.js App Router
+ Aptabase + Next.js App Router
);
}
diff --git a/examples/next-with-pages/next-env.d.ts b/examples/next-with-pages/next-env.d.ts
new file mode 100644
index 0000000..4f11a03
--- /dev/null
+++ b/examples/next-with-pages/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/examples/next-with-pages/next.config.js b/examples/next-with-pages/next.config.js
new file mode 100644
index 0000000..e904157
--- /dev/null
+++ b/examples/next-with-pages/next.config.js
@@ -0,0 +1,9 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+ experimental: {
+ instrumentationHook: true,
+ },
+};
+
+module.exports = nextConfig;
diff --git a/examples/next-with-pages/package.json b/examples/next-with-pages/package.json
new file mode 100644
index 0000000..3e44817
--- /dev/null
+++ b/examples/next-with-pages/package.json
@@ -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": "*"
+ }
+}
diff --git a/examples/next-with-pages/src/components/Counter.tsx b/examples/next-with-pages/src/components/Counter.tsx
new file mode 100644
index 0000000..6e11999
--- /dev/null
+++ b/examples/next-with-pages/src/components/Counter.tsx
@@ -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 (
+
+
Count: {count}
+
+
+
+ );
+}
diff --git a/examples/next-with-pages/src/instrumentation.ts b/examples/next-with-pages/src/instrumentation.ts
new file mode 100644
index 0000000..9de5fce
--- /dev/null
+++ b/examples/next-with-pages/src/instrumentation.ts
@@ -0,0 +1,5 @@
+import { init } from '@aptabase/nextjs';
+
+export function register() {
+ init('A-DEV-0000000000');
+}
diff --git a/examples/next-with-pages/src/pages/_app.tsx b/examples/next-with-pages/src/pages/_app.tsx
new file mode 100644
index 0000000..1372cda
--- /dev/null
+++ b/examples/next-with-pages/src/pages/_app.tsx
@@ -0,0 +1,10 @@
+import { AptabaseProvider } from '@aptabase/nextjs';
+import type { AppProps } from 'next/app';
+
+export default function App({ Component, pageProps }: AppProps) {
+ return (
+
+
+
+ );
+}
diff --git a/examples/next-with-pages/src/pages/_document.tsx b/examples/next-with-pages/src/pages/_document.tsx
new file mode 100644
index 0000000..54e8bf3
--- /dev/null
+++ b/examples/next-with-pages/src/pages/_document.tsx
@@ -0,0 +1,13 @@
+import { Html, Head, Main, NextScript } from 'next/document'
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/examples/next-with-pages/src/pages/api/hello.ts b/examples/next-with-pages/src/pages/api/hello.ts
new file mode 100644
index 0000000..1d31a5c
--- /dev/null
+++ b/examples/next-with-pages/src/pages/api/hello.ts
@@ -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) {
+ await trackEvent('hello', { name: 'John Doe' }, req);
+ res.status(200).json({ name: 'John Doe' });
+}
diff --git a/examples/next-with-pages/src/pages/index.tsx b/examples/next-with-pages/src/pages/index.tsx
new file mode 100644
index 0000000..f568c3c
--- /dev/null
+++ b/examples/next-with-pages/src/pages/index.tsx
@@ -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 (
+ <>
+
+ Create Next App
+
+
+
+
+ Aptabase + Next.js Pages Router
+
+
+ >
+ );
+}
diff --git a/examples/next-with-pages/tsconfig.json b/examples/next-with-pages/tsconfig.json
new file mode 100644
index 0000000..3ca6a9a
--- /dev/null
+++ b/examples/next-with-pages/tsconfig.json
@@ -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"]
+}
diff --git a/package-lock.json b/package-lock.json
index c99fbdc..fc078f4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,6 +33,19 @@
"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": {
"resolved": "packages/nextjs",
"link": true
@@ -590,6 +603,10 @@
"resolved": "examples/next-with-approuter",
"link": true
},
+ "node_modules/next-with-pages": {
+ "resolved": "examples/next-with-pages",
+ "link": true
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
diff --git a/packages/nextjs/src/server.ts b/packages/nextjs/src/server.ts
index 1bbb563..aef9837 100644
--- a/packages/nextjs/src/server.ts
+++ b/packages/nextjs/src/server.ts
@@ -1,9 +1,17 @@
+import { type NextIncomingMessage } from 'next/dist/server/request-meta';
import { headers } from 'next/headers';
-export async function trackEvent(eventName: string, props?: Record): Promise {
+export async function trackEvent(
+ eventName: string,
+ props?: Record,
+ req?: NextIncomingMessage,
+): Promise {
const appKey = globalThis.__APTABASE__.appKey;
if (!appKey) return Promise.resolve();
+ const userAgent = getUserAgent(req);
+ if (!userAgent) return Promise.resolve();
+
const body = JSON.stringify({
timestamp: new Date().toISOString(),
sessionId: 'CHANGE-THIS',
@@ -21,7 +29,7 @@ export async function trackEvent(eventName: string, props?: Record