惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
腾讯CDC
人人都是产品经理
人人都是产品经理
小众软件
小众软件
V
Visual Studio Blog
S
Secure Thoughts
J
Java Code Geeks
V
V2EX
量子位
The Hacker News
The Hacker News
酷 壳 – CoolShell
酷 壳 – CoolShell
Security Latest
Security Latest
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
博客园 - 叶小钗
T
Threat Research - Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Tailwind CSS Blog
Cloudbric
Cloudbric
S
SegmentFault 最新的问题
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Application and Cybersecurity Blog
Application and Cybersecurity Blog
IT之家
IT之家
T
Tenable Blog
S
Security @ Cisco Blogs
月光博客
月光博客
雷峰网
雷峰网
博客园 - 【当耐特】
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
爱范儿
爱范儿
Attack and Defense Labs
Attack and Defense Labs
博客园 - 三生石上(FineUI控件)
Hacker News - Newest:
Hacker News - Newest: "LLM"
有赞技术团队
有赞技术团队
N
News and Events Feed by Topic
阮一峰的网络日志
阮一峰的网络日志
TaoSecurity Blog
TaoSecurity Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Cloudflare Blog
K
Kaspersky official blog

Supabase Blog

AI Agents Know About Supabase. They Don't Always Use It Right. Custom OIDC Providers for Supabase Auth 100,000 GitHub stars Supabase docs over SSH Navigating Regional Network Blocks Supabase Joins the Stripe Projects Developer Preview Log Drains: Now available on Pro Supabase Storage: major performance, security, and reliability updates Supabase incident on February 12, 2026 Hydra joins Supabase X / Twitter OAuth 2.0 is now available for Supabase Auth BKND joins Supabase Supabase is now an official Claude connector Supabase PrivateLink is now available Introducing: Postgres Best Practices When to use Read Replicas vs. bigger compute Introducing TRAE SOLO integration with Supabase Supabase Security Retro: 2025 Sync Stripe Data to Your Supabase Database in One Click Building ChatGPT Apps with Supabase Edge Functions and mcp-use Own Your Observability: Supabase Metrics API Introducing iceberg-js: A JavaScript Client for Apache Iceberg Introducing Supabase for Platforms Adding Async Streaming to Postgres Foreign Data Wrappers Build "Sign in with Your App" using Supabase Auth Introducing Seven New Email Templates for Supabase Auth The new Supabase power for Kiro Introducing Supabase ETL Introducing Analytics Buckets Introducing Vector Buckets Snap, Inc. Launches Snap Cloud, Powered by Supabase Triplit joins Supabase Supabase Series E 1000 Y Combinator Founders Choose Supabase gm 👋 web3, welcome aboard to Sign in with Web3 (Solana, Ethereum) Announcing the Supabase Remote MCP Server Enterprise speed, enterprise standards with Bolt Cloud + Supabase PostgREST 13 Lovable Cloud + Supabase: The Default Platform for AI Builders Processing large jobs with Edge Functions, Cron, and Queues Defense in Depth for MCP Servers OrioleDB Patent: now freely available to the Postgres community Supabase Launch Week 15 Hackathon Winner Announcement The Vibe Coder's Guide to Supabase Environments Testing for Vibe Coders: From Zero to Production Confidence The Vibe Coding Master Checklist Vibe Coding: Best Practices for Prompting Supabase Auth: Build vs. Buy Top 10 Launches of Launch Week 15 Supabase Launch Week 15 Hackathon Storage: 10x Larger Uploads, 3x Cheaper Cached Egress, and 2x Egress Quota Persistent Storage and 97% Faster Cold Starts for Edge Functions Algolia Connector for Supabase New Observability Features in Supabase Improved Security Controls and A New Home for Security Introducing Branching 2.0 Stripe-To-Postgres Sync Engine as standalone Library Supabase Analytics Buckets with Iceberg Support Create a Supabase backend using Figma Make Introducing JWT Signing Keys Supabase UI: Platform Kit Build a Personalized AI Assistant with Postgres Announcing Multigres: Vitess for Postgres Building on open table formats Open Data Standards: Postgres, OTel, and Iceberg Simplifying back-end complexity with Supabase Data APIs PostgreSQL Event Triggers without superuser access Top 10 Launches of Launch Week 14 Supabase MCP Server Data API Routes to Nearest Read Replica Declarative Schemas for Simpler Database Management Realtime: Broadcast from Database Keeping Tabs on What's New in Supabase Studio Edge Functions: Deploy from the Dashboard + Deno 2.1 Automatic Embeddings in Postgres Introducing the Supabase UI Library Supabase Auth: Bring Your Own Clerk Postgres Language Server: Initial Release Migrating from Fauna to Supabase Migrating from the MongoDB Data API to Supabase Dedicated Poolers Postgres as a Graph Database: (Ab)using pgRouting AI Hackathon at Y Combinator Calendars in Postgres using Foreign Data Wrappers Supabase Launch Week 13 Hackathon Winners How to Hack the Base! Running Durable Workflows in Postgres using DBOS database.build v2: Bring-your-own-LLM Restore to a New Project Hack the Base! with Supabase Top 10 Launches of Launch Week 13 Supabase Queues High Performance Disk Supabase Cron Supabase CLI v2: Config as Code Supabase Edge Functions: Introducing Background Tasks, Ephemeral Storage, and WebSockets Supabase AI Assistant v2 OrioleDB Public Alpha Executing Dynamic JavaScript Code on Supabase with Edge Functions ClickHouse Partnership, improved Postgres Replication, and Disk Management
Self-host Maps with Protomaps and Supabase Storage
Brandon Liu, Thor Schaeff · 2024-06-19 · via Supabase Blog

Self-host Maps with Protomaps and Supabase Storage

Protomaps is an open source map of the world, deployable as a single static file on Supabase Storage.

In this tutorial, you will learn to

  • Use Protomaps to excract an area into a static PMTiles file.
  • Upload the PMTiles file to Supabase Storage.
  • Use MapLibre to render the Map onto a Web Page.
  • Use Supabase Edge Functions to restrict File Access.

Protomaps provides a pmtiles CLI that can be used to cut out certain areas from the world map and compress those into a single static file.

For example, we can extract a small area around Utrecht in the Netherlands like this:


_10

pmtiles extract https://build.protomaps.com/20240618.pmtiles my_area.pmtiles --bbox=5.068050,52.112086,5.158424,52.064140


Note: make sure to update the date to the latest daily build!

This will create a my_area.pmtiles file which you can upload to Supabase Storage.

In your Supabase Dashboard navigate to Storage and click "New Bucket" and create a new public bucket called public-maps.

Upload the my_area.pmtiles file created earlier to your public bucket. Once uploaded, click the file and tap "Get URL".

Supabase Storage supports the required HTTP Range Requests out of the box, allowing you to use the public storage URL directly from your maps client.

PMTiles easily works with both MapLibre GL and Leaflet. In our example we wil use MapLibre GL, which is a TypeScript library that uses WebGL to render interactive maps from vector tiles in a browser.

This is a vanilla JS example which uses CDN releases of the libraries. You can very easily adapt it to work with React as well, for example using the react-map-gl library.


_54

<html>

_54

<head>

_54

<title>Overture Places</title>

_54

<meta charset="utf-8" />

_54

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

_54

<link

_54

rel="stylesheet"

_54

href="https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.css"

_54

crossorigin="anonymous"

_54

/>

_54

<script

_54

src="https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.js"

_54

crossorigin="anonymous"

_54

></script>

_54

<script src="https://unpkg.com/protomaps-themes-base@2.0.0-alpha.5/dist/index.js"></script>

_54

<script src="https://unpkg.com/pmtiles@3.0.6/dist/pmtiles.js"></script>

_54

<style>

_54

body {

_54

margin: 0;

_54

}

_54

#map {

_54

height: 100%;

_54

width: 100%;

_54

}

_54

</style>

_54

</head>

_54

<body>

_54

<div id="map"></div>

_54

<script type="text/javascript">

_54

// Add the PMTiles Protocol:

_54

let protocol = new pmtiles.Protocol()

_54

maplibregl.addProtocol('pmtiles', protocol.tile)

_54

_54

// Load the Map tiles directly from Supabase Storage:

_54

const map = new maplibregl.Map({

_54

hash: true,

_54

container: 'map',

_54

style: {

_54

version: 8,

_54

glyphs: 'https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf',

_54

sources: {

_54

protomaps: {

_54

attribution:

_54

'<a href="https://github.com/protomaps/basemaps">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',

_54

type: 'vector',

_54

url: 'pmtiles://https://<your-project-ref>.supabase.co/storage/v1/object/public/public-maps/my_area.pmtiles',

_54

},

_54

},

_54

layers: protomaps_themes_base.default('protomaps', 'dark'),

_54

},

_54

})

_54

</script>

_54

</body>

_54

</html>


A public Supabase Storage bucket allows access from any origin, which might not be ideal for your use case. At the time of writing, you're not able to modify the CORS settings for Supabase Storage buckets, however you can utilize Supabase Edge Functions to restrict access to your PMTiles files, allowing you to even pair it with Supabase Auth to restrict access to certain users for example.

In your Supabase Dashboard, create a new private storage bucket called maps-private and upload your my_area.pmtiles file there. Files in private buckets can only be accessed through either a short-lived signed URL, or by passing the secret service role key as an authorization header. Since our Edge Function is a secure server-side environment, we can utilize the latter approach here.

Using the Supabase CLI, create a new Edge Function by running supabase functions new maps-private, then add the following code to your newly created function:

supabase/functions/maps-private/index.ts


_31

const ALLOWED_ORIGINS = ['http://localhost:8000']

_31

const corsHeaders = {

_31

'Access-Control-Allow-Origin': ALLOWED_ORIGINS.join(','),

_31

'Access-Control-Allow-Headers':

_31

'authorization, x-client-info, apikey, content-type, range, if-match',

_31

'Access-Control-Expose-Headers': 'range, accept-ranges, etag',

_31

'Access-Control-Max-Age': '300',

_31

}

_31

_31

Deno.serve((req) => {

_31

// This is needed if you're planning to invoke your function from a browser.

_31

if (req.method === 'OPTIONS') {

_31

return new Response('ok', { headers: corsHeaders })

_31

}

_31

_31

// Check origin

_31

const origin = req.headers.get('Origin')

_31

_31

if (!origin || !ALLOWED_ORIGINS.includes(origin)) {

_31

return new Response('Not Allowed', { status: 405 })

_31

}

_31

_31

const reqUrl = new URL(req.url)

_31

const url = `${Deno.env.get('SUPABASE_URL')}/storage/v1/object/authenticated${reqUrl.pathname}`

_31

_31

const { method, headers } = req

_31

// Add Auth header

_31

const modHeaders = new Headers(headers)

_31

modHeaders.append('authorization', `Bearer ${Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!}`)

_31

return fetch(url, { method, headers: modHeaders })

_31

})


If you want to further restrict access based on authenticated users, you can pair your Edge Function with Supabase Auth as shown in this example.

Lastly, we need to deploy our Edge Function to Supabase by running supabase functions deploy maps-private --no-verify-jwt. Note that the --no-verify-jwt flag is required if you want to allow public access from your website without any Supabase Auth User.

Now we can simply replace the public storage URL with our Edge Functions URL to proxy the range requests to our private bucket:


_19

// ...

_19

const map = new maplibregl.Map({

_19

hash: true,

_19

container: 'map',

_19

style: {

_19

version: 8,

_19

glyphs: 'https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf',

_19

sources: {

_19

protomaps: {

_19

attribution:

_19

'<a href="https://github.com/protomaps/basemaps">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',

_19

type: 'vector',

_19

url: 'pmtiles://https://<project_ref>.supabase.co/functions/v1/maps-private/my_area.pmtiles',

_19

},

_19

},

_19

layers: protomaps_themes_base.default('protomaps', 'dark'),

_19

},

_19

})

_19

// ...


Now go ahead and serve your index.html file, for example via Python SimpleHTTPServer: python3 -m http.server and admire your beautiful map on localhost:8000!

As you might know, I'm a big React Native fan, and when writing this tutorial I was very excited about making this work in Expo mobile apps also.

Unfortunately, at the time of writing, custom protocols are not supported in maplibre-react-native. There is an issues tracking this here, so if there are any native mobile wizards out there, I'd very much appreciate your contributions!

In the meantime, however, the Expo team had a great idea, what about leveraging React DOM components, which are currently experimentally supported in Expo SDK 52 preview.

This approach allows you to utilize react-map-gl and maplibre-gl-js across your Expo web and mobile apps.

Follow the steps to install the canary release.

To render a React component to the DOM, add the 'use dom' directive to the top of the web component file:


_47

'use dom'

_47

_47

import 'text-encoding-polyfill'

_47

import { useEffect } from 'react'

_47

import Map from 'react-map-gl'

_47

import maplibregl from 'maplibre-gl'

_47

import 'maplibre-gl/dist/maplibre-gl.css'

_47

import { Protocol } from 'pmtiles'

_47

_47

export default function MapBox(_) {

_47

useEffect(() => {

_47

let protocol = new Protocol()

_47

maplibregl.addProtocol('pmtiles', protocol.tile)

_47

return () => {

_47

maplibregl.removeProtocol('pmtiles')

_47

}

_47

}, [])

_47

_47

return (

_47

<div style={{ width: '100%', height: '100%' }}>

_47

<Map

_47

style={{ width: '100%', height: 900 }}

_47

mapStyle={{

_47

version: 8,

_47

sources: {

_47

sample: {

_47

type: 'vector',

_47

url: 'pmtiles://https://r2-public.protomaps.com/protomaps-sample-datasets/cb_2018_us_zcta510_500k.pmtiles',

_47

},

_47

},

_47

layers: [

_47

{

_47

id: 'zcta',

_47

source: 'sample',

_47

'source-layer': 'zcta',

_47

type: 'line',

_47

paint: {

_47

'line-color': '#999',

_47

},

_47

},

_47

],

_47

}}

_47

mapLib={maplibregl}

_47

/>

_47

</div>

_47

)

_47

}


Inside the native component file, import the web component to use it:


_21

import { StatusBar } from 'expo-status-bar'

_21

import { StyleSheet, Text, View } from 'react-native'

_21

import Map from './Map.jsx'

_21

_21

export default function App() {

_21

return (

_21

<View style={styles.container}>

_21

<Map dom={{ autoSize: true }} />

_21

<StatusBar style="auto" />

_21

</View>

_21

)

_21

}

_21

_21

const styles = StyleSheet.create({

_21

container: {

_21

flex: 1,

_21

backgroundColor: '#fff',

_21

alignItems: 'stretch',

_21

justifyContent: 'center',

_21

},

_21

})


Protomaps is a fantastic open source project that allows you to host your own Google Maps alternative on Supabase Storage. You can further extend this with powerful PostGIS capabilities to programmatically generate Vector Tiles which we will explore in the next post in this series. So make sure you subscribe to our Twitter and YouTube channels to not miss out! See you then!